home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / giochi / amigainf.lha / Inform / library / parser.h < prev    next >
Text File  |  1995-08-02  |  119KB  |  3,555 lines

  1. ! ----------------------------------------------------------------------------
  2. !  "PARSER":  the core of the Inform library, and the parser
  3. !
  4. !  Supplied for use with Inform 5
  5. !
  6. !  (c) Graham Nelson 1993, 1994, 1995, but freely usable (see documentation)
  7. ! ----------------------------------------------------------------------------
  8.  
  9. Constant LibSerial "950703";
  10. Constant LibRelease "5/11";
  11.  
  12. ! ----------------------------------------------------------------------------
  13. !  Attribute and property definitions
  14. !  The compass, directions, darkness and player objects
  15. !  Definitions of fake actions
  16. !  Library global variables
  17. !  Private parser variables
  18. !  Keyboard reading
  19. !  Parser, level 0: outer shell, conversation, errors
  20. !                1: grammar lines
  21. !                2: tokens
  22. !                3: object lists
  23. !                4: scope and ambiguity resolving
  24. !                5: object comparisons
  25. !                6: word comparisons
  26. !                7: reading words and moving tables about
  27. !  Main game loop
  28. !  Action processing
  29. !  Menus
  30. !  Time: timers and daemons
  31. !  Considering light
  32. !  Changing player personality
  33. !  Printing short names
  34. ! ----------------------------------------------------------------------------
  35.  
  36. ! ----------------------------------------------------------------------------
  37. ! Declare the attributes and properties.  Note that properties "preroutine"
  38. ! and "postroutine" default to $ffff which forces them to be two bytes long:
  39. ! similarly, "timeleft" sometimes needs to be over 256, so it's flagged long
  40. ! ----------------------------------------------------------------------------
  41.  
  42. System_file;
  43.  
  44. Attribute animate;
  45. Attribute clothing;
  46. Attribute concealed;
  47. Attribute container;
  48. Attribute direction;
  49. Attribute door;
  50. Attribute edible;
  51. Attribute enterable;
  52. Attribute female;
  53. Attribute general;
  54. Attribute light;
  55. Attribute lockable;
  56. Attribute locked;
  57. Attribute moved;
  58. Attribute on;
  59. Attribute open;
  60. Attribute openable;
  61. Attribute proper;
  62. Attribute scenery;
  63. Attribute scored;
  64. Attribute static;
  65. Attribute supporter;
  66. Attribute switchable;
  67. Attribute talkable;
  68. Attribute transparent;
  69. Attribute visited;
  70. Attribute workflag;
  71. Attribute worn;
  72.  
  73. Attribute absent alias female;      !  Please, no psychoanalysis
  74.  
  75. Constant NULL $ffff;
  76.  
  77. Property additive before NULL;
  78. Property additive after  NULL;
  79. Property additive life   NULL;
  80.  
  81. Property long n_to;  Property long s_to; !  Slightly wastefully, these are
  82. Property long e_to;  Property long w_to; !  long (they might be routines)
  83. Property long ne_to; Property long se_to;
  84. Property long nw_to; Property long sw_to;
  85. Property long u_to;  Property long d_to;
  86. Property long in_to; Property long out_to;
  87.  
  88. Property door_to     alias n_to;     !  For economy: these properties are
  89. Property when_closed alias s_to;     !  used only by objects which
  90. Property with_key    alias e_to;     !  aren't rooms
  91. Property door_dir    alias w_to;
  92. Property invent      alias u_to;
  93. Property plural      alias d_to;
  94. Property add_to_scope alias se_to;
  95. Property list_together alias sw_to;
  96.  
  97. Property long initial;
  98. Property when_open   alias initial;
  99. Property when_on     alias initial;
  100. Property when_off    alias when_closed;
  101. Property long description;
  102. Property additive describe NULL;
  103. Property article "a";
  104.  
  105. Property cant_go "You can't go that way.";
  106.  
  107. Property long found_in;         !  For fiddly reasons this can't alias
  108.  
  109. Property long time_left;
  110. Property long number;
  111. Property additive time_out NULL;
  112. Property daemon alias time_out;
  113. Property additive each_turn NULL;
  114.  
  115. Property capacity 100;
  116.  
  117. Property long short_name 0;
  118. Property long parse_name 0;
  119.  
  120. ! The following definitions, commented out, define pre-Inform 5.2 and now
  121. ! obselete names as aliases for the standard names:
  122.  
  123. ! Property preroutine alias before; Property desc        alias description;
  124. ! Property postroutine alias after; Property longdesc    alias description;
  125. ! Property liferoutine alias life;  Property timeleft    alias time_left;
  126. ! Property initpos     alias initial;
  127. ! Property portalto    alias door_to;
  128. ! Property closedpos   alias when_closed;
  129. ! Property dirprop     alias door_dir;
  130. ! Property cantgo      alias cant_go;
  131. ! Attribute portal alias door;
  132.  
  133. ! ----------------------------------------------------------------------------
  134. ! Construct the compass - a dummy object containing the directions, which also
  135. ! represent the walls in whatever room the player is in (these are given the
  136. ! general-purpose "number" property for the programmer's convenience)
  137. ! ----------------------------------------------------------------------------
  138.  
  139. Object compass "compass" nothing has concealed;
  140.  
  141. #IFNDEF WITHOUT_DIRECTIONS;
  142. Object n_obj "north wall" compass      
  143.   with name "n" "north" "wall",       article "the", door_dir n_to, number 0
  144.   has  scenery;
  145. Object s_obj "south wall" compass      
  146.   with name "s" "south" "wall",       article "the", door_dir s_to, number 0
  147.   has  scenery;
  148. Object e_obj "east wall" compass      
  149.   with name "e" "east" "wall",        article "the", door_dir e_to, number 0
  150.    has  scenery;
  151. Object w_obj "west wall" compass       
  152.   with name "w" "west" "wall",        article "the", door_dir w_to, number 0
  153.    has  scenery;
  154. Object ne_obj "northeast wall" compass 
  155.   with name "ne" "northeast" "wall",  article "the", door_dir ne_to, number 0
  156.   has  scenery;
  157. Object se_obj "southeast wall" compass
  158.   with name "se" "southeast" "wall",  article "the", door_dir se_to, number 0
  159.   has  scenery;
  160. Object nw_obj "northwest wall" compass
  161.   with name "nw" "northwest" "wall",  article "the", door_dir nw_to, number 0
  162.   has  scenery;
  163. Object sw_obj "southwest wall" compass
  164.   with name "sw" "southwest" "wall",  article "the", door_dir sw_to, number 0
  165.   has  scenery;
  166. Object u_obj "ceiling" compass         
  167.   with name "u" "up" "ceiling",       article "the", door_dir u_to, number 0
  168.    has  scenery;
  169. Object d_obj "floor" compass
  170.   with name "d" "down" "floor",       article "the", door_dir d_to, number 0
  171.    has  scenery;
  172. #ENDIF;
  173. Object out_obj "outside" compass
  174.   with                                article "the", door_dir out_to, number 0
  175.    has  scenery;
  176. Object in_obj "inside" compass
  177.   with                                article "the", door_dir in_to, number 0
  178.    has  scenery;
  179.  
  180. ! ----------------------------------------------------------------------------
  181. ! The other dummy object is "Darkness", not really a place but it has to be
  182. ! an object so that the name on the status line can be "Darkness":
  183. ! we also create the player object
  184. ! ----------------------------------------------------------------------------
  185.  
  186. Object thedark "Darkness"
  187.   with description "It is pitch dark, and you can't see a thing.";
  188.  
  189. Object selfobj "yourself"
  190.   with description "As good-looking as ever.", number 0,
  191.        before NULL, after NULL, life NULL, each_turn NULL,
  192.        time_out NULL, describe NULL, capacity 100,
  193.        parse_name 0, short_name 0
  194.   has  concealed animate proper transparent;
  195.  
  196. ! ----------------------------------------------------------------------------
  197. ! Fake actions: treated as if they were actions, when calling
  198. ! routines attached to objects
  199. ! ----------------------------------------------------------------------------
  200.  
  201. Fake_Action LetGo;
  202. Fake_Action Receive;
  203. Fake_Action ThrownAt;
  204. Fake_Action Order;
  205. Fake_Action TheSame;
  206. Fake_Action PluralFound;
  207. Fake_Action Miscellany;
  208. Fake_Action Prompt;
  209.  
  210. ! ----------------------------------------------------------------------------
  211. ! Globals: note that the first one defined gives the status line place, the
  212. ! next two the score/turns
  213. ! ----------------------------------------------------------------------------
  214.  
  215. Global location = 1;
  216. Global sline1 = 0;
  217. Global sline2 = 0;
  218.  
  219. Global the_time = NULL;
  220. Global time_rate = 1;
  221. Global time_step = 0;
  222.  
  223. Global score = 0;
  224. Global turns = 1;
  225. Global player;
  226.  
  227. Global lightflag = 1;
  228. Global real_location = thedark;
  229. Global print_player_flag = 0;
  230. Global deadflag = 0;
  231.  
  232. Global inventory_stage = 1;
  233. global c_style;
  234. global lt_value;
  235. global listing_together;
  236. global listing_size;
  237.  
  238. Global transcript_mode = 0;
  239.  
  240. Global last_score = 0;
  241. Global notify_mode = 1;       ! Score notification
  242.  
  243. Global places_score = 0;
  244. Global things_score = 0;
  245. Global lookmode = 1;
  246. Global lastdesc = 0;
  247.  
  248. Global top_object = 0;
  249.  
  250. ! ----------------------------------------------------------------------------
  251. ! Parser variables accessible to the rest of the game
  252. ! ----------------------------------------------------------------------------
  253.  
  254. Global buffer          string 120;   ! Text buffer
  255. Global parse           string 64;    ! List of parsed addresses of words
  256. Global inputobjs       data 32;      ! To hold parameters
  257. Global toomany_flag    = 0;          ! Flag for "take all made too many"
  258. Global actor           = 0;          ! Person asked to do something
  259. Global action          = 0;          ! Thing he is asked to do
  260. Global inp1            = 0;          ! First parameter
  261. Global inp2            = 0;          ! Second parameter
  262. Global self            = 0;          ! Object whose routines are being run
  263. Global noun            = 0;          ! First noun
  264. Global second          = 0;          ! Second noun
  265. Global multiple_object data 128;     ! List of multiple parameters
  266. Global special_word    = 0;          ! Dictionary address of "special"
  267. Global special_number  = 0;          ! The number, if a number was typed
  268. Global special_number2 = 0;          ! Second number, if two numbers typed
  269. Global parsed_number   = 0;          ! For user-supplied parsing routines
  270. global multiflag;                    ! Multiple-object flag
  271. global notheld_mode  = 0;            ! To do with implicit taking
  272. global onotheld_mode = 0;            !
  273. global meta;                         ! Verb is a meta-command (such as "save")
  274. global reason_code;                  ! Reason for calling a life
  275. global sw__var         = 0;          ! Switch variable (used for embeddeds)
  276. global consult_from;                 ! Word that "consult" topic starts on
  277. global consult_words;                ! ...and number of words in topic
  278.  
  279. #IFV5;
  280. global undo_flag = 0;                ! Can the interpreter provide "undo"?
  281. #ENDIF;
  282.  
  283. global parser_trace = 0;             ! Set this to 1 to make the parser trace
  284.                                      ! tokens and lines
  285. global debug_flag = 0;               ! For debugging information
  286. global lm_n;                         ! Parameters for LibraryMessages
  287. global lm_o;
  288.  
  289. ! ----------------------------------------------------------------------------
  290. ! Main (putting it here ensures it is the first routine, as it must be)
  291. ! ----------------------------------------------------------------------------
  292.  
  293. [ Main;
  294.   player=selfobj;
  295.   PlayTheGame();
  296. ];
  297.  
  298. ! ----------------------------------------------------------------------------
  299. ! The parser, beginning with variables private to itself:
  300. ! ----------------------------------------------------------------------------
  301.  
  302. global buffer2   string 120;    ! Buffers for supplementary questions
  303. global parse2    string 64;     !
  304. global parse3    string 64;     !
  305.  
  306. global wn;                      ! Word number (counts from 1)
  307. global num_words;               ! Number of words typed
  308. global verb_word;               ! Verb word (eg, take in "take all" or
  309.                                 ! "dwarf, take all") - address in dictionary
  310. global verb_wordnum;            ! and the number in typing order (eg, 1 or 3)
  311.  
  312. global multi_mode;              ! Multiple mode
  313. global multi_wanted;            ! Number of things needed in multitude
  314. global multi_had;               ! Number of things actually found
  315. global multi_context;           ! What token the multi-object was accepted for
  316.  
  317. global pattern data 16;         ! For the current pattern match
  318. global pcount;                  ! and a marker within it
  319. global pattern2 data 16;        ! And another, which stores the best match
  320. global pcount2;                 ! so far
  321.  
  322. global parameters;              ! Parameters (objects) entered so far
  323. global params_wanted;           ! Number needed (may change in parsing)
  324.  
  325. global nsns;                    ! Number of special_numbers entered so far
  326.  
  327. global inferfrom;               ! The point from which the rest of the
  328.                                 ! command must be inferred
  329. global inferword;               ! And the preposition inferred
  330.  
  331. global oops_from = 0;           ! The "first mistake" point, where oops acts
  332. global saved_oops = 0;          ! Used in working this out
  333. global oops_heap data 10;       ! Used temporarily by "oops" routine
  334.  
  335. Constant MATCH_LIST_SIZE 128;
  336. global match_list    data MATCH_LIST_SIZE;
  337.                                 ! An array of matched objects so far
  338. global match_classes data MATCH_LIST_SIZE;
  339.                                 ! An array of equivalence classes for them
  340. global number_matched;          ! How many items in it?  (0 means none)
  341. global number_of_classes;       ! How many equivalence classes?
  342. global match_length;            ! How many typed words long are these matches?
  343. global match_from;              ! At what word of the input do they begin?
  344.  
  345. global parser_action;           ! For the use of the parser when calling
  346. global parser_one;              ! user-supplied routines
  347. global parser_two;              !
  348.  
  349. global vague_word;              ! Records which vague word ("it", "them", ...)
  350.                                 ! caused an error
  351. global vague_obj;               ! And what it was thought to refer to
  352.  
  353. global itobj=0;                 ! The object which is currently "it"
  354. global himobj=0;                ! The object which is currently "him"
  355. global herobj=0;                ! The object which is currently "her"
  356.  
  357. global lookahead;               ! The token after the object now being matched
  358. global indef_mode;              ! "Indefinite" mode - ie, "take a brick" is in
  359.                                 ! this mode
  360. global indef_type;              ! Bit-map holding types of specification
  361. global indef_wanted;            ! Number of items wanted (100 for all)
  362. global indef_guess_p;           ! Plural-guessing flag
  363. global allow_plurals;           ! Whether they are presently allowed or not
  364. global not_holding;             ! Object to be automatically taken as an
  365.                                 ! implicit command
  366. global kept_results data 32;    ! The delayed command (while the take happens)
  367.  
  368. global saved_wn;                ! These are temporary variables for Parser()
  369. global saved_token;             ! (which hasn't enough spare local variables)
  370.  
  371. global held_back_mode = 0;      ! Flag: is there some input from last time
  372. global hb_wn = 0;               ! left over?  (And a save value for wn)
  373.  
  374. global best_etype;              ! Error number used within parser
  375. global etype;                   ! Error number used for individual lines
  376.  
  377. global last_command_from;       ! For sorting out "then again"
  378. global last_command_to;         !
  379.  
  380. global token_was;               ! For noun filtering by user routines
  381.  
  382. global advance_warning;         ! What a later-named thing will be
  383.  
  384. global placed_in_flag;          ! To do with PlaceInScope
  385. global length_of_noun;          ! Set by NounDomain to number of words in noun
  386.  
  387. global action_to_be;            ! So the parser can "cheat" in one case
  388. global dont_infer;              ! Another dull flag
  389.  
  390. global et_flag = 0;             ! Processing "each_turn" mode
  391.  
  392. global scope_token;             ! For scope:Routine tokens
  393. global scope_error;
  394. global scope_stage;
  395.  
  396. global ats_flag = 0;            ! For AddToScope routines
  397. global ats_hls;                 !
  398.  
  399. #IFV5;
  400. global just_undone = 0;         ! Can't have two successive UNDOs
  401. #ENDIF;
  402.  
  403. ! ----------------------------------------------------------------------------
  404. !  The comma_word is a special word, used to substitute commas in the input
  405. ! ----------------------------------------------------------------------------
  406.  
  407. Constant comma_word 'xcomma';
  408.  
  409. ! ----------------------------------------------------------------------------
  410. !  In Advanced games only, the DrawStatusLine routine does just that: this is
  411. !  provided explicitly so that it can be Replace'd to change the style, and
  412. !  as written it emulates the ordinary Standard game status line, which is
  413. !  drawn in hardware
  414. ! ----------------------------------------------------------------------------
  415. #IFV5;
  416. [ DrawStatusLine i;
  417.    @split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
  418.    spaces (0->33)-1;
  419.    @set_cursor 1 2;  PrintShortName(location);
  420.    if ((0->1)&2 == 0)
  421.    {   @set_cursor 1 51; print "Score: ", sline1;
  422.        @set_cursor 1 64; print "Moves: ", sline2;
  423.    }
  424.    else
  425.    {   @set_cursor 1 51; print "Time: ";
  426.        i=sline1%12; if (i<10) print " ";
  427.        if (i==0) i=12;
  428.        print i, ":";
  429.        if (sline2<10) print "0";
  430.        print sline2;
  431.        if ((sline1/12) > 0) print " pm"; else print " am";
  432.    }
  433.    @set_cursor 1 1; style roman; @set_window 0;
  434. ];
  435. #ENDIF;
  436.  
  437. ! ----------------------------------------------------------------------------
  438. !  The Keyboard routine actually receives the player's words,
  439. !  putting the words in "a_buffer" and their dictionary addresses in
  440. !  "a_table".  It is assumed that the table is the same one on each
  441. !  (standard) call.
  442. !
  443. !  It can also be used by miscellaneous routines in the game to ask
  444. !  yes-no questions and the like, without invoking the rest of the parser.
  445. !
  446. !  Return the number of words typed
  447. ! ----------------------------------------------------------------------------
  448.  
  449. [ Keyboard  a_buffer a_table  nw i w x1 x2;
  450.  
  451.     DisplayStatus();
  452.     .FreshInput;
  453.  
  454. !  Save the start of the table, in case "oops" needs to restore it
  455. !  to the previous time's table
  456.  
  457.     for (i=0:i<10:i++) oops_heap->i = a_table->i;
  458.  
  459. !  In case of an array entry corruption that shouldn't happen, but would be
  460. !  disastrous if it did:
  461.  
  462.    a_buffer->0 = 120;
  463.    a_table->0 = 64;
  464.  
  465. !  Print the prompt, and read in the words and dictionary addresses
  466.  
  467.     L__M(##Prompt);
  468.     #IFV3; read a_buffer a_table; #ENDIF;
  469.     temp_global = 0;
  470.     #IFV5; read a_buffer a_table DrawStatusLine; #ENDIF;
  471.     nw=a_table->1;
  472.  
  473. !  If the line was blank, get a fresh line
  474.     if (nw == 0)
  475.     { print "I beg your pardon?^"; jump FreshInput; }
  476.  
  477. !  Unless the opening word was "oops" or its abbreviation "o", return
  478.  
  479.     w=a_table-->1;
  480.     if (w == #n$o or 'oops') jump DoOops;
  481.  
  482. #IFV5;
  483. !  Undo handling
  484.  
  485.     if (w == 'undo')
  486.     {   if (turns==1)
  487.         {   print "[You can't ~undo~ what hasn't been done!]^";
  488.             jump FreshInput;
  489.         }
  490.         if (undo_flag==0)
  491.         {   print "[Your interpreter does not provide ~undo~.  Sorry!]^";
  492.             jump FreshInput;
  493.         }
  494.         if (undo_flag==1) jump UndoFailed;
  495.         if (just_undone==1)
  496.         {   print "[Can't ~undo~ twice in succession.  Sorry!]^";
  497.             jump FreshInput;
  498.         }
  499.         @restore_undo i;
  500.         if (i==0)
  501.         {   .UndoFailed;
  502.             print "~Undo~ failed.  [Not every interpreter provides it.]^";
  503.         }
  504.         jump FreshInput;
  505.     }
  506.     @save_undo i;
  507.     just_undone=0;
  508.     undo_flag=2;
  509.     if (i==-1) undo_flag=0;
  510.     if (i==0) undo_flag=1;
  511.     if (i==2)
  512.     {   style bold;
  513.         print (name) location, "^";
  514.         style roman;
  515.         print "[Previous turn undone]^";
  516.         just_undone=1;
  517.         jump FreshInput;
  518.     }
  519. #ENDIF;
  520.  
  521.     return nw;
  522.  
  523.     .DoOops;
  524.     if (oops_from == 0)
  525.     {   print "Sorry, that can't be corrected.^"; jump FreshInput; }
  526.     if (nw == 1)
  527.     {   print "Think nothing of it.^"; jump FreshInput; }
  528.     if (nw > 2)
  529.     {   print "~Oops~ can only correct a single word.^"; jump FreshInput; }
  530.  
  531. !  So now we know: there was a previous mistake, and the player has
  532. !  attempted to correct a single word of it.
  533. !
  534. !  Oops is very primitive: it gets the text buffer wrong, for instance.
  535. !
  536. !  Take out the 4-byte table entry for the supplied correction:
  537. !  restore the 10 bytes at the front of the table, which were over-written
  538. !  by what the user just typed: and then replace the oops_from word entry
  539. !  with the correction one.
  540. !
  541.     x1=a_table-->3; x2=a_table-->4;
  542.     for (i=0:i<10:i++) a_table->i = oops_heap->i;
  543.     w=2*oops_from - 1;
  544.     a_table-->w = x1;
  545.     a_table-->(w+1) = x2;
  546.  
  547.     return nw;
  548. ];
  549.  
  550. Constant STUCK_PE     1;
  551. Constant UPTO_PE      2;
  552. Constant NUMBER_PE    3;
  553. Constant CANTSEE_PE   4;
  554. Constant TOOLIT_PE    5;
  555. Constant NOTHELD_PE   6;
  556. Constant MULTI_PE     7;
  557. Constant MMULTI_PE    8;
  558. Constant VAGUE_PE     9;
  559. Constant EXCEPT_PE    10;
  560. Constant ANIMA_PE     11;
  561. Constant VERB_PE      12;
  562. Constant SCENERY_PE   13;
  563. Constant ITGONE_PE    14;
  564. Constant JUNKAFTER_PE 15;
  565. Constant TOOFEW_PE    16;
  566. Constant NOTHING_PE   17;
  567. Constant ASKSCOPE_PE  18;
  568.  
  569. ! ----------------------------------------------------------------------------
  570. !  The Parser routine is the heart of the parser.
  571. !
  572. !  It returns only when a sensible request has been made, and puts into the
  573. !  "results" buffer:
  574. !
  575. !  Word 0 = The action number
  576. !  Word 1 = Number of parameters
  577. !  Words 2, 3, ... = The parameters (object numbers), but
  578. !                    00 means "multiple object list goes here"
  579. !                    01 means "special word goes here"
  580. !
  581. !  (Some of the global variables above are really local variables for this
  582. !  routine, because the Z-machine only allows up to 15 local variables per
  583. !  routine, and Parser runs out.)
  584. !
  585. !  To simplify the picture a little, a rough map of this routine is:
  586. !
  587. !  (A)    Get the input, do "oops" and "again"
  588. !  (B)    Is it a direction, and so an implicit "go"?  If so go to (K)
  589. !  (C)    Is anyone being addressed?
  590. !  (D)    Get the verb: try all the syntax lines for that verb
  591. !  (E)        Go through each token in the syntax line
  592. !  (F)           Check (or infer) an adjective
  593. !  (G)            Check to see if the syntax is finished, and if so return
  594. !  (H)    Cheaply parse otherwise unrecognised conversation and return
  595. !  (I)    Print best possible error message
  596. !  (J)    Retry the whole lot
  597. !  (K)    Last thing: check for "then" and further instructions(s), return.
  598. !
  599. !  The strategic points (A) to (K) are marked in the commentary.
  600. !
  601. !  Note that there are three different places where a return can happen.
  602. !
  603. ! ----------------------------------------------------------------------------
  604.  
  605. [ Parser  results   syntax line num_lines line_address i j
  606.                     token l m;
  607.  
  608. !  **** (A) ****
  609.  
  610. !  Firstly, in "not held" mode, we still have a command left over from last
  611. !  time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
  612. !  last time, with "eat biscuit" tucked away until now).  So we return that.
  613.  
  614.     if (notheld_mode==1)
  615.     {   for (i=0:i<8:i++) results-->i=kept_results-->i;
  616.         notheld_mode=0; rtrue;
  617.     }
  618.  
  619.     if (held_back_mode==1)
  620.     {   held_back_mode=0;
  621.         for (i=0:i<64:i++) parse->i=parse2->i;
  622.         new_line;
  623.         jump ReParse;
  624.     }
  625.  
  626.   .ReType;
  627.  
  628.     Keyboard(buffer,parse);
  629.  
  630.   .ReParse;
  631.  
  632. !  Initially assume the command is aimed at the player, and the verb
  633. !  is the first word
  634.  
  635.     num_words=parse->1;
  636.  
  637.     if (parser_trace>=4)
  638.     {   print "[ ", num_words, " to parse: ";
  639.         for (i=1:i<=num_words:i++)
  640.         {   j=parse-->((i-1)*2+1);
  641.             if (j == 0) print "? ";
  642.             else
  643.             {   if (UnsignedCompare(j, 0-->4)>=0
  644.                     && UnsignedCompare(j, 0-->2)<0) print_addr j;
  645.                 else print j; print " ";
  646.             }
  647.         }
  648.         print "]^";
  649.     }
  650.  
  651.     verb_wordnum=1;
  652.     actor=player;
  653.     token_was = 0; ! In case we're still in "user-filter" mode from last round
  654.     scope_token = 0;
  655.  
  656. !  Begin from what we currently think is the verb word
  657.  
  658.   .BeginCommand;
  659.     wn=verb_wordnum;
  660.     verb_word = NextWord();
  661.  
  662. !  Now try for "again" or "g", which are special cases:
  663. !  don't allow "again" if nothing has previously been typed;
  664. !  simply copy the previous parse table and ReParse with that
  665.  
  666.     if (verb_word==#n$g) verb_word='again';
  667.     if (verb_word=='again')
  668.     {   if (actor~=player)
  669.         {   print "To repeat a command like ~frog, jump~, just say \
  670.              ~again~, not ~frog, again~.^"; jump ReType; }
  671.         if (parse3->1==0)
  672.         {   print "You can hardly repeat that.^"; jump ReType; }
  673.         for (i=0:i<64:i++) parse->i=parse3->i;
  674.         jump ReParse;
  675.     }
  676.  
  677. !  Save the present parse table in case of an "again" next time
  678.  
  679.     if (verb_word~='again')
  680.         for (i=0:i<64:i++)
  681.             parse3->i=parse->i;
  682.  
  683. !  If the first word is not recognised, give a user-supplied routine
  684. !  the chance to work out what it is:
  685.  
  686.     if (verb_word==0 && actor==player) verb_word=UnknownVerb(verb_word);
  687.  
  688. !  But if it still isn't, then it can't be either the name of
  689. !  an animate creature or a verb, so give an error at once.
  690.  
  691.     if (verb_word==0)
  692.     {   best_etype=VERB_PE;
  693.         jump GiveError;
  694.     }
  695.  
  696. !  **** (B) ****
  697.  
  698. !  If the first word is not listed as a verb, it must be a direction
  699. !  or the name of someone to talk to
  700. !  (NB: better avoid having a Mr Take or Mrs Inventory around...)
  701.  
  702.     if (((verb_word->#dict_par1) & 1) == 0)
  703.     {   
  704.  
  705. !  So is the first word an object contained in the special object "compass"
  706. !  (i.e., a direction)?  This needs use of NounDomain, a routine which
  707. !  does the object matching, returning the object number, or 0 if none found,
  708. !  or 1000 if it has restructured the parse table so that the whole parse
  709. !  must be begun again...
  710.  
  711.         wn=verb_wordnum;
  712.         l=NounDomain(compass,0,0); if (l==1000) jump ReParse;
  713.  
  714. !  If it is a direction, send back the results:
  715. !  action=GoSub, no of arguments=1, argument 1=the direction.
  716.  
  717.         if (l~=0)
  718.         {   results-->0 = ##Go;
  719.             results-->1 = 1;
  720.             results-->2 = l;
  721.             jump LookForMore;
  722.         }
  723.  
  724. !  **** (C) ****
  725.  
  726. !  Only check for a comma (a "someone, do something" command) if we are
  727. !  not already in the middle of one.  (This simplification stops us from
  728. !  worrying about "robot, wizard, you are an idiot", telling the robot to
  729. !  tell the wizard that she is an idiot.)
  730.  
  731.         if (actor==player)
  732.             for (j=2:j<=num_words:j++)
  733.             {   i=NextWord(); if (i==comma_word) jump Conversation;
  734.             }
  735.  
  736. !  The initial word was something, but not a verb - give UnknownVerb
  737. !  a chance to restore it to health.
  738.          
  739.         if (actor==player)
  740.         {   verb_word=UnknownVerb(verb_word);
  741.             if (verb_word~=0) jump VerbAccepted;
  742.         }
  743.  
  744.         best_etype=VERB_PE; jump GiveError;
  745.  
  746. !  NextWord nudges the word number wn on by one each time, so we've now
  747. !  advanced past a comma.  (A comma is a word all on its own in the table.)
  748.  
  749.       .Conversation;
  750.         j=wn-1;
  751.         if (j==1) { print "You can't begin with a comma.^"; jump ReType; }
  752.  
  753. !  Use NounDomain (in the context of "animate creature") to see if the
  754. !  words make sense as the name of someone held or nearby
  755.  
  756.         wn=1; lookahead=1;
  757.         l=NounDomain(player,location,6); if (l==1000) jump ReParse;
  758.  
  759.         if (l==0) { print "You seem to want to talk to someone, \
  760.                          but I can't see whom.^"; jump ReType; }
  761.  
  762. !  The object addressed must at least be "talkable" if not actually "animate"
  763. !  (the distinction allows, for instance, a microphone to be spoken to,
  764. !  without the parser thinking that the microphone is human).
  765.  
  766.         if (l hasnt animate && l hasnt talkable)
  767.         {   print "You can't talk to "; DefArt(l); print ".^"; jump ReType; }
  768.  
  769. !  Check that there aren't any mystery words between the end of the person's
  770. !  name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
  771.  
  772.         if (wn~=j)
  773.         {   print "To talk to someone, try ~someone, hello~ or some such.^";
  774.             jump ReType;
  775.         }
  776.  
  777. !  The player has now successfully named someone.  Adjust "him", "her", "it":
  778.  
  779.         ResetVagueWords(l);
  780.  
  781. !  Set the global variable "actor", adjust the number of the first word,
  782. !  and begin parsing again from there.
  783.  
  784.         verb_wordnum=j+1; actor=l;
  785.         jump BeginCommand;
  786.     }
  787.  
  788. !  **** (D) ****
  789.  
  790.    .VerbAccepted;
  791.  
  792. !  We now definitely have a verb, not a direction, whether we got here by the
  793. !  "take ..." or "person, take ..." method.  Get the meta flag for this verb:
  794.  
  795.     meta=((verb_word->#dict_par1) & 2)/2;
  796.  
  797. !  Now let i be the corresponding verb number, stored in the dictionary entry
  798. !  (in a peculiar 255-n fashion for traditional Infocom reasons)...
  799.  
  800.     i=$ff-(verb_word->#dict_par2);
  801.  
  802. !  ...then look up the i-th entry in the verb table, whose address is at word
  803. !  7 in the Z-machine (in the header), so as to get the address of the syntax
  804. !  table for the given verb...
  805.  
  806.     syntax=(0-->7)-->i;
  807.  
  808. !  ...and then see how many lines (ie, different patterns corresponding to the
  809. !  same verb) are stored in the parse table...
  810.  
  811.     num_lines=(syntax->0)-1;
  812.  
  813. !  ...and now go through them all, one by one.
  814. !  To prevent vague_word 0 being misunderstood,
  815.  
  816.    vague_word='it'; vague_obj=itobj;
  817.  
  818.    if (parser_trace>=1)
  819.    {    print "[Parsing for the verb '"; print_addr verb_word;
  820.         print "' (", num_lines+1, " lines)]^";
  821.    }
  822.  
  823.    best_etype=STUCK_PE;
  824. !  "best_etype" is the current failure-to-match error - it is by default
  825. !  the least informative one, "don't understand that sentence"
  826.  
  827.  
  828. !  **** (E) ****
  829.  
  830.     for (line=0:line<=num_lines:line++)
  831.     {   line_address = syntax+1+line*8;
  832.  
  833.         if (parser_trace>=1)
  834.         {   print "[Line ", line, ": ", line_address->0, " parameters: ";
  835.             for (pcount=1:pcount<=6:pcount++)
  836.             {   token=line_address->pcount;
  837.                 print token, " ";
  838.             }
  839.             print " -> action ", line_address->7, "]^";
  840.         }
  841.  
  842. !  We aren't in "not holding" or inferring modes, and haven't entered
  843. !  any parameters on the line yet, or any special numbers; the multiple
  844. !  object is still empty.
  845.  
  846.         not_holding=0;
  847.         inferfrom=0;
  848.         parameters=0;
  849.         params_wanted = line_address->0;
  850.         nsns=0;
  851.         multiple_object-->0 = 0;
  852.         etype=STUCK_PE;
  853.         action_to_be = line_address->7;
  854.  
  855. !  Put the word marker back to just after the verb
  856.  
  857.         wn=verb_wordnum+1;
  858.  
  859. !  An individual "line" contains six tokens...  There's a preliminary pass
  860. !  first, to parse late tokens early if necessary (because of mi or me)
  861.  
  862.         advance_warning=-1;
  863.         for (i=0,pcount=1:pcount<=6:pcount++)
  864.         {   scope_token=0;
  865.             token=line_address->pcount;
  866.             if (token<180) i++;
  867.             if (token==4 or 5 && i==1)
  868.             {   if (parser_trace>=2) print " [Trying look-ahead]^";
  869.                 pcount++;
  870.                 while (pcount<=6 && line_address->pcount>=180) pcount++;
  871.                 token=line_address->(pcount-1);
  872.                 if (token>=180)
  873.                 {   j=AdjectiveAddress(token);
  874.  
  875.                     !  Now look for word with j, move wn, parse next
  876.                     !  token...
  877.                     while (wn <= num_words)
  878.                     {   if (NextWord()==j)
  879.                         {   l = NounDomain(location,actor,token);
  880.                             if (parser_trace>=2)
  881.                             {   print " [Forward token parsed: ";
  882.                                 if (l==1000) print "re-parse request]^";
  883.                                 if (l==1) print "but multiple found]^";
  884.                                 if (l==0) print "hit error ", etype, "]^";
  885.                             }
  886.                             if (l==1000) jump ReParse;
  887.                             if (l>=2)
  888.                             {   advance_warning = l;
  889.                                 if (parser_trace>=3)
  890.                                 {   DefArt(l); print "]^";
  891.                                 }
  892.                             }
  893.                         }
  894.                     }
  895.                 }
  896.             }
  897.         }
  898.  
  899. !  And now start again, properly, forearmed or not as the case may be.
  900.  
  901.         not_holding=0;
  902.         inferfrom=0;
  903.         parameters=0;
  904.         nsns=0;
  905.         multiple_object-->0 = 0;
  906.         etype=STUCK_PE;
  907.         action_to_be = line_address->7;
  908.         wn=verb_wordnum+1;
  909.  
  910. !  "Pattern" gradually accumulates what has been recognised so far,
  911. !  so that it may be reprinted by the parser later on
  912.  
  913.         for (pcount=1:pcount<=6:pcount++)
  914.         {   pattern-->pcount=0; scope_token=0;
  915.  
  916.             token=line_address->pcount;
  917.  
  918.             if (parser_trace>=2)
  919.             {   print " [Token ",pcount, " is ", token, ": ";
  920.                 if (token<16)
  921.                 {   if (token==0) print "<noun> or null";
  922.                     if (token==1) print "<held>";
  923.                     if (token==2) print "<multi>";
  924.                     if (token==3) print "<multiheld>";
  925.                     if (token==4) print "<multiexcept>";
  926.                     if (token==5) print "<multiinside>";
  927.                     if (token==6) print "<creature>";
  928.                     if (token==7) print "<special>";
  929.                     if (token==8) print "<number>";
  930.                 }
  931.                 if (token>=16 && token<48)
  932.                     print "<noun filter by routine ",token-16, ">";
  933.                 if (token>=48 && token<80)
  934.                     print "<general parse by routine ",token-48, ">";
  935.                 if (token>=80 && token<128)
  936.                     print "<scope parse by routine ",token-80, ">";
  937.                 if (token>=128 && token<180)
  938.                     print "<noun filter by attribute ",token-128, ">";
  939.                 if (token>180)
  940.                 {   print "<adjective ",255-token, " '";
  941.                     print_addr AdjectiveAddress(token); print "'>";
  942.                 }
  943.                 print " at word number ", wn, "]^";
  944.             }
  945.  
  946. !  Lookahead is set to the token after this one, or 8 if there isn't one.
  947. !  (Complicated because the line is padded with 0's.)
  948.  
  949.             m=pcount+1; lookahead=8;
  950.             if (m<=6) lookahead=line_address->m;
  951.             if (lookahead==0)
  952.             {   m=parameters; if (token<=7) m++;
  953.                 if (m>=params_wanted) lookahead=8;
  954.             }
  955.  
  956. !  **** (F) ****
  957.  
  958. !  When the token is a large number, it must be an adjective:
  959. !  remember the adjective number in the "pattern".
  960.  
  961.             if (token>180)
  962.             {   pattern-->pcount = 1000+token;
  963.  
  964. !  If we've run out of the player's input, but still have parameters to
  965. !  specify, we go into "infer" mode, remembering where we are and the
  966. !  adjective we are inferring...
  967.  
  968.                 if (wn > num_words)
  969.                 {   if (inferfrom==0 && parameters<params_wanted)
  970.                     { inferfrom=pcount; inferword=token; }
  971.  
  972. !  Otherwise, this line must be wrong.
  973.  
  974.                     if (inferfrom==0) break;
  975.                 }
  976.  
  977. !  Whereas, if the player has typed something here, see if it is the
  978. !  required adjective... if it's wrong, the line must be wrong,
  979. !  but if it's right, the token is passed (jump to finish this token).
  980.  
  981.                 if (wn <= num_words && token~=Adjective()) break;
  982.                 jump TokenPassed;
  983.             }
  984.  
  985. !  **** (G) ****
  986. !  Check now to see if the player has entered enough parameters...
  987. !  (since params_wanted is the number of them)
  988.  
  989.             if (parameters == params_wanted)
  990.             {  
  991.  
  992. !  If the player has entered enough parameters already but there's still
  993. !  text to wade through: store the pattern away so as to be able to produce
  994. !  a decent error message if this turns out to be the best we ever manage,
  995. !  and in the mean time give up on this line
  996.  
  997. !  However, if the superfluous text begins with a comma, "and" or "then" then
  998. !  take that to be the start of another instruction
  999.  
  1000.                 if (wn <= num_words)
  1001.                 {   l=NextWord();
  1002.                     if (l=='then' or comma_word)
  1003.                     {   held_back_mode=1; hb_wn=wn-1; }
  1004.                     else
  1005.                     {   for (m=0:m<8:m++) pattern2-->m=pattern-->m;
  1006.                         pcount2=pcount;
  1007.                         etype=UPTO_PE; break;
  1008.                     }
  1009.                 }
  1010.  
  1011. !  Now, we may need to revise the multiple object because of the single one
  1012. !  we now know (but didn't when the list was drawn up).
  1013.  
  1014.                 if (parameters>=1 && results-->2 == 0)
  1015.                 {   l=ReviseMulti(results-->3);
  1016.                     if (l~=0) { etype=l; break; }
  1017.                 }
  1018.                 if (parameters>=2 && results-->3 == 0)
  1019.                 {   l=ReviseMulti(results-->2);
  1020.                     if (l~=0) { etype=l; break; }
  1021.                 }
  1022.  
  1023.                 if (parser_trace>=1)
  1024.                     print "[Line successfully parsed]^";
  1025.  
  1026. !  At this point the line has worked out perfectly, and it's a matter of
  1027. !  sending the results back...
  1028. !  ...pausing to explain any inferences made (using the pattern)...
  1029.  
  1030.                 if (inferfrom~=0)
  1031.                 {   print "("; PrintCommand(inferfrom,1); print ")^";
  1032.                 }
  1033.  
  1034. !  ...and to copy the action number, and the number of parameters...
  1035.  
  1036.                 results-->1 = params_wanted;
  1037.                 results-->0 = line_address->7;
  1038.  
  1039. !  ...and to reset "it"-style objects to the first of these parameters, if
  1040. !  there is one (and it really is an object)...
  1041.  
  1042.                 if (parameters > 0 && results-->2 >= 2)
  1043.                     ResetVagueWords(results-->2);
  1044.  
  1045. !  ...and declare the user's input to be error free...
  1046.  
  1047.                 oops_from = 0;
  1048.  
  1049. !  ...and worry about the case where an object was allowed as a parameter
  1050. !  even though the player wasn't holding it and should have been: in this
  1051. !  event, keep the results for next time round, go into "not holding" mode,
  1052. !  and for now tell the player what's happening and return a "take" request
  1053. !  instead...
  1054.  
  1055.                 if (not_holding~=0 && actor==player)
  1056.                 {   notheld_mode=1;
  1057.                     for (i=0:i<8:i++) kept_results-->i = results-->i;
  1058.                     results-->0 = ##Take;
  1059.                     results-->1 = 1;
  1060.                     results-->2 = not_holding;
  1061.                     print "(first taking "; DefArt(not_holding); print ")^";
  1062.                 }
  1063.  
  1064. !  (Notice that implicit takes are only generated for the player, and not
  1065. !  for other actors.  This avoids entirely logical, but misleading, text
  1066. !  being printed.)
  1067.  
  1068. !  ...and finish.
  1069.  
  1070.                 if (held_back_mode==1) { wn=hb_wn; jump LookForMore; }
  1071.                 rtrue;
  1072.             }
  1073.  
  1074. !  Otherwise, the player still has at least one parameter to specify: an
  1075. !  object of some kind is expected, and this we hand over to:
  1076.  
  1077.             l=ParseObjectList(results,token);
  1078.             if (parser_trace>=3)
  1079.             {   print "  [Parse object list replied with";
  1080.                 if (l==1000) print " re-parse request]^";
  1081.                 if (l==0) print " token failed, error type ", etype, "]^";
  1082.                 if (l==1) print " token accepted]^";
  1083.             }
  1084.             if (l==1000) jump ReParse;
  1085.             if (l==0)    break;
  1086.  
  1087. !  The token has been successfully passed; we are ready for the next.
  1088.  
  1089.             .TokenPassed;
  1090.         }
  1091.  
  1092. !  But if we get here it means that the line failed somewhere, so we continue
  1093. !  the outer for loop and try the next line...
  1094.  
  1095.         if (etype>best_etype) best_etype=etype;
  1096.    }
  1097.  
  1098. !  So that if we get here, each line for the specified verb has failed.
  1099.  
  1100. !  **** (H) ****
  1101.  
  1102.   .GiveError;
  1103.         etype=best_etype;
  1104.  
  1105. !  Errors are handled differently depending on who was talking.
  1106.  
  1107. !  If the command was addressed to somebody else (eg, "dwarf, sfgh") then
  1108. !  it is taken as conversation which the parser has no business in disallowing.
  1109. !  In order to make it easier for the host game to work out what was said, the
  1110. !  "verb" word (eg, "sfgh") is parsed as a number and as a dictionary entry,
  1111. !  and the parser returns as if the player had typed
  1112. !
  1113. !     answer sfgh to dwarf   
  1114. !
  1115. !  with the globals special_word and special_number set accordingly.
  1116.  
  1117. !  (This is convenient for, say, "computer, 2451" or "guard, blue").
  1118.  
  1119.     if (actor~=player)
  1120.     {   special_number=TryNumber(verb_wordnum);
  1121.         wn=verb_wordnum;
  1122.         special_word=NextWord();
  1123.         action=##Answer;
  1124.         inp1=1; inp2=actor; actor=player;
  1125.         rtrue;
  1126.     }
  1127.  
  1128. !  **** (I) ****
  1129.  
  1130. !  If the player was the actor (eg, in "take dfghh") the error must be printed,
  1131. !  and fresh input called for.  In three cases the oops word must be jiggled.
  1132.  
  1133.     if (ParserError(etype)~=0) jump ReType;
  1134.  
  1135.     if (etype==STUCK_PE)
  1136.              {   print "I didn't understand that sentence.^"; oops_from=1; }
  1137.     if (etype==UPTO_PE)
  1138.              {   print "I only understood you as far as wanting to ";
  1139.                  for (m=0:m<8:m++) pattern-->m = pattern2-->m;
  1140.                  pcount=pcount2; PrintCommand(0,1); print ".^";
  1141.              }
  1142.     if (etype==NUMBER_PE)
  1143.                  print "I didn't understand that number.^";
  1144.     if (etype==CANTSEE_PE)
  1145.              {   print "You can't see any such thing.^";
  1146.                  oops_from=saved_oops; }
  1147.     if (etype==TOOLIT_PE)
  1148.                  print "You seem to have said too little!^";
  1149.     if (etype==NOTHELD_PE)
  1150.              {   print "You aren't holding that!^";
  1151.                  oops_from=saved_oops; }
  1152.     if (etype==MULTI_PE)
  1153.                  print "You can't use multiple objects with that verb.^";
  1154.     if (etype==MMULTI_PE)
  1155.                  print "You can only use multiple objects once on a line.^";
  1156.     if (etype==VAGUE_PE)
  1157.              {   print "I'm not sure what ~"; print_addr vague_word;
  1158.                  print "~ refers to.^"; }
  1159.     if (etype==EXCEPT_PE)
  1160.                  print "You excepted something not included anyway!^";
  1161.     if (etype==ANIMA_PE)
  1162.                  print "You can only do that to something animate.^";
  1163.     if (etype==VERB_PE)
  1164.                  print "That's not a verb I recognise.^";
  1165.     if (etype==SCENERY_PE)
  1166.                  print "That's not something you need to refer to \
  1167.                         in the course of this game.^";
  1168.     if (etype==ITGONE_PE)
  1169.              {   print "You can't see ~"; print_addr vague_word;
  1170.                  print "~ ("; DefArt(vague_obj); print ") at the moment.^"; }
  1171.     if (etype==JUNKAFTER_PE)
  1172.                  print "I didn't understand the way that finished.^";
  1173.     if (etype==TOOFEW_PE)
  1174.              {   if (multi_had==0) print "None";
  1175.                  else { print "Only "; EnglishNumber(multi_had); }
  1176.                  print " of those ";
  1177.                  if (multi_had==1) print "is"; else print "are";
  1178.                  print " available.^"; }
  1179.     if (etype==NOTHING_PE)
  1180.              {   if (multi_wanted==100) print "Nothing to do!^";
  1181.                  else print "There are none at all available!^";  }
  1182.     if (etype==ASKSCOPE_PE)
  1183.     {            scope_stage=3; indirect(scope_error); }
  1184.  
  1185. !  **** (J) ****
  1186.  
  1187. !  And go (almost) right back to square one...
  1188.  
  1189.     jump ReType;
  1190.  
  1191. !  ...being careful not to go all the way back, to avoid infinite repetition
  1192. !  of a deferred command causing an error.
  1193.  
  1194.  
  1195. !  **** (K) ****
  1196.  
  1197. !  At this point, the return value is all prepared, and we are only looking
  1198. !  to see if there is a "then" followed by subsequent instruction(s).
  1199.     
  1200.    .LookForMore;
  1201.  
  1202.    if (wn>num_words) rtrue;
  1203.  
  1204.    i=NextWord();
  1205.    if (i=='then' || i==comma_word)
  1206.    {   if (wn>num_words)
  1207.        { parse2->1=(parse2->1)-1; held_back_mode = 0; rtrue; }
  1208.        if (actor==player) j=0; else j=verb_wordnum-1;
  1209.        last_command_from = j+1; last_command_to = wn-2;
  1210.        i=NextWord();
  1211.        if (i=='again' or #n$g)
  1212.        {   for (i=0: i<j: i++)
  1213.            {   parse2-->(2*i+1) = parse-->(2*i+1);
  1214.                parse2-->(2*i+2) = parse-->(2*i+2);
  1215.            }
  1216.            for (i=last_command_from:i<=last_command_to:i++, j++)
  1217.            {   parse2-->(2+2*j) = parse-->(2*i);
  1218.                parse2-->(1+2*j) = parse-->(2*i-1);
  1219.            }
  1220.            for (i=wn:i<=num_words:i++, j++)
  1221.            {   parse2-->(2+2*j) = parse-->(2*i);
  1222.                parse2-->(1+2*j) = parse-->(2*i-1);
  1223.            }
  1224.            parse2->1=j; held_back_mode = 1; rtrue;
  1225.        }
  1226.        else wn--;
  1227.        for (i=0: i<j: i++)
  1228.        {   parse2-->(2*i+1) = parse-->(2*i+1);
  1229.            parse2-->(2*i+2) = parse-->(2*i+2);
  1230.        }
  1231.        for (i=wn:i<=num_words:i++, j++)
  1232.        {   parse2-->(2+2*j) = parse-->(2*i);
  1233.            parse2-->(1+2*j) = parse-->(2*i-1);
  1234.        }
  1235.        parse2->1=j; held_back_mode = 1; rtrue;
  1236.    }
  1237.    best_etype=UPTO_PE; jump GiveError;
  1238. ];
  1239.  
  1240. ! ----------------------------------------------------------------------------
  1241. !  NumberWord - fairly self-explanatory
  1242. ! ----------------------------------------------------------------------------
  1243.  
  1244. [ NumberWord o;
  1245.   if (o=='one') return 1;
  1246.   if (o=='two') return 2;
  1247.   if (o=='three') return 3;
  1248.   if (o=='four') return 4;
  1249.   if (o=='five') return 5;
  1250.   if (o=='six') return 6;
  1251.   if (o=='seven') return 7;
  1252.   if (o=='eight') return 8;
  1253.   if (o=='nine') return 9;
  1254.   if (o=='ten') return 10;
  1255.   if (o=='eleven') return 11;
  1256.   if (o=='twelve') return 12;
  1257.   if (o=='thirteen') return 13;
  1258.   if (o=='fourteen') return 14;
  1259.   if (o=='fifteen') return 15;
  1260.   if (o=='sixteen') return 16;
  1261.   if (o=='seventeen') return 17;
  1262.   if (o=='eighteen') return 18;
  1263.   if (o=='nineteen') return 19;
  1264.   if (o=='twenty') return 20;
  1265.   return 0;
  1266. ];
  1267.  
  1268. ! ----------------------------------------------------------------------------
  1269. !  Descriptors()
  1270. !
  1271. !  Handles descriptive words like "my", "his", "another" and so on.
  1272. !  Skips "the", and leaves wn pointing to the first misunderstood word.
  1273. !
  1274. !  Allowed to set up for a plural only if allow_p is set
  1275. !
  1276. !  Returns error number, or 0 if no error occurred
  1277. ! ----------------------------------------------------------------------------
  1278.  
  1279. Constant OTHER_BIT    1;     !  These will be used in Adjudicate()
  1280. Constant MY_BIT       2;     !  to disambiguate choices
  1281. Constant THAT_BIT     4;
  1282. Constant PLURAL_BIT   8;
  1283. Constant ITS_BIT     16;
  1284. Constant HIS_BIT     32;
  1285. Constant LIT_BIT     64;
  1286. Constant UNLIT_BIT  128;
  1287.  
  1288. [ Descriptors context  o flag n;
  1289.  
  1290.    indef_mode=0; indef_type=0; indef_wanted=0; indef_guess_p=0;
  1291.  
  1292.    for (flag=1:flag==1:)
  1293.    {   o=NextWord(); flag=0;
  1294.        if (o=='the') flag=1;
  1295.        if (o==#n$a or 'an' or 'any' || o=='either' or 'anything')
  1296.                             { indef_mode=1; flag=1; }
  1297.        if (o=='another' or 'other')
  1298.                             { indef_mode=1; flag=1;
  1299.                               indef_type = indef_type | OTHER_BIT; }
  1300.        if (o=='my' or 'this' or 'these')
  1301.                             { indef_mode=1; flag=1;
  1302.                               indef_type = indef_type | MY_BIT; }
  1303.        if (o=='that' or 'those')
  1304.                             { indef_mode=1; flag=1;
  1305.                               indef_type = indef_type | THAT_BIT; }
  1306.        if (o=='its')
  1307.                             { indef_mode=1; flag=1;
  1308.                               indef_type = indef_type | ITS_BIT; }
  1309.        if (o=='his' or 'your')
  1310.                             { indef_mode=1; flag=1;
  1311.                               indef_type = indef_type | HIS_BIT; }
  1312.        if (o=='lit' or 'lighted')
  1313.                             { indef_mode=1; flag=1;
  1314.                               indef_type = indef_type | LIT_BIT; }
  1315.        if (o=='unlit')
  1316.                             { indef_mode=1; flag=1;
  1317.                               indef_type = indef_type | UNLIT_BIT; }
  1318.        if (o=='all' or 'each' or 'every' || o=='everything')
  1319.                             { indef_mode=1; flag=1; indef_wanted=100;
  1320.                               indef_type = indef_type | PLURAL_BIT; }
  1321.        if (allow_plurals==1)
  1322.        {   n=NumberWord(o);
  1323.            if (n>1)         { indef_guess_p=1;
  1324.                               indef_mode=1; flag=1; indef_wanted=n;
  1325.                               indef_type = indef_type | PLURAL_BIT; }
  1326.        }
  1327.        if (flag==1 && NextWord() ~= 'of') wn--;  ! Skip 'of' after these
  1328.    }
  1329.    wn--;
  1330.    if ((indef_wanted > 0) && (context<2 || context>5)) return MULTI_PE;
  1331.    return 0;
  1332. ];
  1333.  
  1334. ! ----------------------------------------------------------------------------
  1335. !  ParseObjectList: Parses tokens 0 to 179, from the current word number wn
  1336. !
  1337. !  Returns:
  1338. !    1000 for "reconstructed input, please re-parse from scratch"
  1339. !    1    for "token accepted"
  1340. !    0    for "token failed"
  1341. !
  1342. !  (A)            Preliminaries and special/number tokens
  1343. !  (B)            Actual object names (mostly subcontracted!)
  1344. !  (C)            and/but and so on
  1345. !  (D)            Returning an accepted token
  1346. !
  1347. ! ----------------------------------------------------------------------------
  1348.  
  1349. [ ParseObjectList results token  l o i j k
  1350.                                  and_parity single_object desc_wn many_flag;
  1351.  
  1352.     many_flag=0; and_parity=1; dont_infer=0;
  1353.  
  1354. !  **** (A) ****
  1355. !  We expect to find a list of objects next in what the player's typed.
  1356.  
  1357.   .ObjectList;
  1358.  
  1359.    if (parser_trace>=3) print "  [Object list from word ", wn, "]^";
  1360.  
  1361. !  Take an advance look at the next word: if it's "it" or "them", and these
  1362. !  are unset, set the appropriate error number and give up on the line
  1363. !  (if not, these are still parsed in the usual way - it is not assumed
  1364. !  that they still refer to something in scope)
  1365.  
  1366.     o=NextWord(); wn--;
  1367.     if (o=='it' or 'them')
  1368.     {   vague_word=o; vague_obj=itobj;
  1369.         if (itobj==0) { etype=VAGUE_PE; return 0; }
  1370.     }
  1371.     if (o=='him')
  1372.     {   vague_word=o; vague_obj=himobj;
  1373.         if (himobj==0) { etype=VAGUE_PE; return 0; }
  1374.     }
  1375.     if (o=='her')
  1376.     {   vague_word=o; vague_obj=herobj;
  1377.         if (herobj==0) { etype=VAGUE_PE; return 0; }
  1378.     }
  1379.     if (o=='me' or 'myself' or 'self')
  1380.     {   vague_word=o; vague_obj=player;
  1381.     }
  1382.  
  1383. !  Firstly, get rid of tokens 7 and 8 ("special" and "number"), and
  1384. !  tokens which are entirely handed out to outside routines
  1385.  
  1386.     if (token==7)
  1387.     {   l=TryNumber(wn);
  1388.         if (l~=-1000)
  1389.         {   if (nsns==0) special_number=l; else special_number2=l;
  1390.             nsns++;
  1391.             if (parser_trace>=3)
  1392.                 print "  [Read special as the number ", l, "]^";
  1393.         }
  1394.         if (parser_trace>=3)
  1395.             print "  [Read special word at word number ", wn, "]^";
  1396.         special_word=NextWord(); single_object=1; jump PassToken;
  1397.     }
  1398.     if (token==8)
  1399.     {   l=TryNumber(wn++);
  1400.         if (l==-1000) { etype=NUMBER_PE; rfalse; }
  1401.         if (parser_trace>=3) print "  [Read number as ", l, "]^";
  1402.         if (nsns++==0) special_number=l; else special_number2=l;
  1403.         single_object=1; jump PassToken;
  1404.     }
  1405.  
  1406.     if (token>=48 && token<80)
  1407.     {   l=indirect(#preactions_table-->(token-48));
  1408.         if (parser_trace>=3)
  1409.             print "  [Outside parsing routine returned ", l, "]^";
  1410.         if (l<0) rfalse;
  1411.         if (l==0) { params_wanted--; rtrue; }  ! An adjective after all...
  1412.         if (l==1)
  1413.         {   if (nsns==0) special_number=parsed_number;
  1414.             else special_number2=parsed_number;
  1415.             nsns++;
  1416.         }
  1417.         single_object=l; jump PassToken;
  1418.     }
  1419.  
  1420.     if (token>=80 && token<128)
  1421.     {   scope_token = #preactions_table-->(token-80);
  1422.         scope_stage = 1;
  1423.         l=indirect(scope_token);
  1424.         if (parser_trace>=3)
  1425.             print "  [Scope routine returned multiple-flag of ", l, "]^";
  1426.         if (l==1) token=2; else token=0;
  1427.     }
  1428.  
  1429.     token_was=0;
  1430.     if (token>=16)
  1431.     {   token_was = token;
  1432.         token=0;
  1433.     }
  1434.  
  1435. !  Otherwise, we have one of the tokens 0 to 6, all of which really do mean
  1436. !  that objects are expected.
  1437.  
  1438. !  So now we parse any descriptive words
  1439.  
  1440.     allow_plurals = 1; desc_wn = wn;
  1441.     .TryAgain;
  1442.  
  1443.     l=Descriptors(token); if (l~=0) { etype=l; return 0; }
  1444.  
  1445. !  **** (B) ****
  1446.  
  1447. !  This is an actual specified object, and is therefore where a typing error
  1448. !  is most likely to occur, so we set:
  1449.  
  1450.     oops_from=wn;
  1451.  
  1452. !  In either case below we use NounDomain, giving it the token number as
  1453. !  context, and two places to look: among the actor's possessions, and in the
  1454. !  present location.  (Note that the order depends on which is likeliest.)
  1455.  
  1456. !  So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
  1457. !  but we may well be dealing with multiple objects
  1458.  
  1459.     if (token~=1)
  1460.     {   i=multiple_object-->0;
  1461.         if (parser_trace>=3)
  1462.             print "  [Calling NounDomain on location and actor]^";
  1463.         l=NounDomain(location, actor, token);
  1464.         if (l==1000) return l;                    ! Reparse after Q&A
  1465.         if (l==0) { etype=CantSee(); jump FailToken; }  ! Choose best error
  1466.         if (parser_trace>=3)
  1467.         {   if (l>1)
  1468.             {   print "  [ND returned "; DefArt(l); print "]^"; }
  1469.             else
  1470.             {   print "  [ND appended to the multiple object list:^";
  1471.                 k=multiple_object-->0;
  1472.                 for (j=i+1:j<=k:j++)
  1473.                 {   print "  Entry ", j, ": "; CDefArt(multiple_object-->j);
  1474.                     print " (", multiple_object-->j, ")^";
  1475.                 }
  1476.                 print "  List now has size ", k, "]^";
  1477.             }
  1478.         }
  1479.         if (l==1)
  1480.         {   if (many_flag==0)
  1481.             {   many_flag=1;
  1482.             }
  1483.             else                                  ! Merge with earlier ones
  1484.             {   k=multiple_object-->0;            ! (with either parity)
  1485.                 multiple_object-->0 = i;
  1486.                 for (j=i+1:j<=k:j++)
  1487.                 {   if (and_parity==1) MultiAdd(multiple_object-->j);
  1488.                     else MultiSub(multiple_object-->j);
  1489.                 }
  1490.         if (parser_trace>=3)
  1491.             print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
  1492.             }
  1493.         }
  1494.         else
  1495.         {   if (token==6 && l hasnt animate)      ! Animation is required
  1496.             {   etype=ANIMA_PE; jump FailToken; } ! for token 6
  1497.             if (many_flag==0)
  1498.                 single_object = l;
  1499.             else
  1500.             {   if (and_parity==1) MultiAdd(l); else MultiSub(l);
  1501.                 if (parser_trace>=3)
  1502.                 {   print "  [Combining "; DefArt(single_object);
  1503.                     print " with list]^";
  1504.                 }
  1505.             }
  1506.         }
  1507.     }
  1508.  
  1509. !  Case 2: token is "held" (which fortunately can't take multiple objects)
  1510. !  and may generate an implicit take
  1511.  
  1512.     if (token==1)
  1513.     {   l=NounDomain(actor,location,token);       ! Same as above...
  1514.         if (l==1000) return l;
  1515.         if (l==0) { etype=CantSee(); return l; }
  1516.  
  1517. !  ...until it produces something not held by the actor.  Then an implicit
  1518. !  take must be tried.  If this is already happening anyway, things are too
  1519. !  confused and we have to give up (but saving the oops marker so as to get
  1520. !  it on the right word afterwards).
  1521. !  The point of this last rule is that a sequence like
  1522. !
  1523. !      > read newspaper
  1524. !      (taking the newspaper first)
  1525. !      The dwarf unexpectedly prevents you from taking the newspaper!
  1526. !
  1527. !  should not be allowed to go into an infinite repeat - read becomes
  1528. !  take then read, but take has no effect, so read becomes take then read...
  1529. !  Anyway for now all we do is record the number of the object to take.
  1530.  
  1531.         o=parent(l);
  1532.         if (o~=actor)
  1533.         {   if (notheld_mode==1)
  1534.             {   saved_oops=oops_from; etype=NOTHELD_PE; jump FailToken;
  1535.             }
  1536.             not_holding = l;
  1537.             if (parser_trace>=3)
  1538.             {   print "  [Allowing object "; DefArt(l); print " for now]^";
  1539.             }
  1540.         }
  1541.         single_object = l;
  1542.     }
  1543.  
  1544. !  The following moves the word marker to just past the named object...
  1545.  
  1546.     wn = oops_from + match_length;
  1547.  
  1548. !  **** (C) ****
  1549.  
  1550. !  Object(s) specified now: is that the end of the list, or have we reached
  1551. !  "and", "but" and so on?  If so, create a multiple-object list if we
  1552. !  haven't already (and are allowed to).
  1553.  
  1554.     .NextInList;
  1555.  
  1556.     o=NextWord();
  1557.  
  1558.     if (o=='and' or 'but' or 'except' || o==comma_word)
  1559.     {
  1560.         if (parser_trace>=3)
  1561.         {   print "  [Read '"; print_addr o; print "']^";
  1562.         }
  1563.  
  1564.         if (token<2 || token>=6) { etype=MULTI_PE; jump FailToken; }
  1565.  
  1566.         if (o=='but' or 'except') and_parity = 1-and_parity;
  1567.  
  1568.         if (many_flag==0)
  1569.         {   multiple_object-->0 = 1;
  1570.             multiple_object-->1 = single_object;
  1571.             many_flag=1;
  1572.             if (parser_trace>=3)
  1573.             {   print "  [Making new list from ";
  1574.                 DefArt(single_object); print "]^";
  1575.             }
  1576.         }
  1577.         dont_infer = 1; inferfrom=0;              ! Don't print (inferences)
  1578.         jump ObjectList;                          ! And back around
  1579.     }
  1580.  
  1581.     wn--;   ! Word marker back to first not-understood word
  1582.  
  1583. !  **** (D) ****
  1584.  
  1585. !  Happy or unhappy endings:
  1586.  
  1587.     .PassToken;
  1588.  
  1589.     if (many_flag==1) single_object=0;
  1590.     else
  1591.     {   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  1592.         {   if (indef_wanted<100 && indef_wanted>1)
  1593.             {   multi_had=1; multi_wanted=indef_wanted;
  1594.                 etype=TOOFEW_PE;
  1595.                 jump FailToken;
  1596.             }
  1597.         }
  1598.     }
  1599.     results-->(parameters+2) = single_object;
  1600.     parameters++;
  1601.     pattern-->pcount = single_object;
  1602.     return 1;
  1603.  
  1604.     .FailToken;
  1605.  
  1606. !  If we were only guessing about it being a plural, try again but only
  1607. !  allowing singulars (so that words like "six" are not swallowed up as
  1608. !  Descriptors)
  1609.  
  1610.     if (allow_plurals==1 && indef_guess_p==1)
  1611.     {   allow_plurals=0; wn=desc_wn; jump TryAgain;
  1612.     }
  1613.     return 0;
  1614. ];
  1615.  
  1616. ! ----------------------------------------------------------------------------
  1617. !  NounDomain does the most substantial part of parsing an object name.
  1618. !
  1619. !  It is given two "domains" - usually a location and then the actor who is
  1620. !  looking - and a context (i.e. token type), and returns:
  1621. !
  1622. !   0    if no match at all could be made,
  1623. !   1    if a multiple object was made,
  1624. !   k    if object k was the one decided upon,
  1625. !   1000 if it asked a question of the player and consequently rewrote all
  1626. !        the player's input, so that the whole parser should start again
  1627. !        on the rewritten input.
  1628. !
  1629. !   In the case when it returns 1<k<1000, it also sets the variable
  1630. !   length_of_noun to the number of words in the input text matched to the
  1631. !   noun.
  1632. !   In the case k=1, the multiple objects are added to multiple_object by
  1633. !   hand (not by MultiAdd, because we want to allow duplicates).
  1634. ! ----------------------------------------------------------------------------
  1635.  
  1636. [ NounDomain domain1 domain2 context  first_word i j k l oldw
  1637.                                       answer_words marker;
  1638.  
  1639.   if (parser_trace>=4) print "   [NounDomain called at word ", wn, "^";
  1640.  
  1641.   match_length=0; number_matched=0; match_from=wn; placed_in_flag=0;
  1642.  
  1643.   SearchScope(domain1, domain2, context);
  1644.  
  1645.   if (parser_trace>=4) print "   [ND made ", number_matched, " matches]^";
  1646.  
  1647.   wn=match_from+match_length;
  1648.  
  1649. !  If nothing worked at all, leave with the word marker skipped past the
  1650. !  first unmatched word...
  1651.  
  1652.   if (number_matched==0) { wn++; rfalse; }
  1653.  
  1654. !  Suppose that there really were some words being parsed (i.e., we did
  1655. !  not just infer).  If so, and if there was only one match, it must be
  1656. !  right and we return it...
  1657.  
  1658.   if (match_from <= num_words)
  1659.   {   if (number_matched==1) { i=match_list-->0; return i; }
  1660.  
  1661. !  ...now suppose that there was more typing to come, i.e. suppose that
  1662. !  the user entered something beyond this noun.  Use the lookahead token
  1663. !  to check that if an adjective comes next, it is the right one.  (If
  1664. !  not then there must be a mistake like "press red buttno" where "red"
  1665. !  has been taken for the noun in the mistaken belief that "buttno" is
  1666. !  some preposition or other.)
  1667. !
  1668. !  If nothing ought to follow, then similarly there must be a mistake,
  1669. !  (unless what does follow is just a full stop, and or comma)
  1670.  
  1671.       if (wn<=num_words)
  1672.       {   i=NextWord(); wn--;
  1673.           if ((i~='and' or comma_word or 'then')
  1674.               && (i~='but' or 'except'))
  1675.           {   if (lookahead==8) rfalse;
  1676.               if (lookahead>8)
  1677.               {   if (lookahead~=Adjective())
  1678.                   { wn--; if (parser_trace>=3)
  1679.                     print "   [ND failed at lookahead at word ", wn, "]^";
  1680.                     rfalse;
  1681.                   }
  1682.                   wn--;
  1683.               }
  1684.           }
  1685.       }
  1686.   }
  1687.  
  1688. !  Now look for a good choice, if there's more than one choice...
  1689.  
  1690.   number_of_classes=0;
  1691.   
  1692.   if (number_matched==1) i=match_list-->0;
  1693.   if (number_matched>1)
  1694.   {   i=Adjudicate(context);
  1695.       if (i==-1) rfalse;
  1696.       if (i==1) rtrue;       !  Adjudicate has made a multiple
  1697.                              !  object, and we pass it on
  1698.   }
  1699.  
  1700. !  If i is non-zero here, one of two things is happening: either
  1701. !  (a) an inference has been successfully made that object i is
  1702. !      the intended one from the user's specification, or
  1703. !  (b) the user finished typing some time ago, but we've decided
  1704. !      on i because it's the only possible choice.
  1705. !  In either case we have to keep the pattern up to date,
  1706. !  note that an inference has been made and return.
  1707. !  (Except, we don't note which of a pile of identical objects.)
  1708.  
  1709.   if (i~=0)
  1710.   {   if (dont_infer==1) return i;
  1711.       if (inferfrom==0) inferfrom=pcount;
  1712.       pattern-->pcount = i;
  1713.       return i;
  1714.   }
  1715.  
  1716. !  If we get here, there was no obvious choice of object to make.  If in
  1717. !  fact we've already gone past the end of the player's typing (which
  1718. !  means the match list must contain every object in scope, regardless
  1719. !  of its name), then it's foolish to give an enormous list to choose
  1720. !  from - instead we go and ask a more suitable question...
  1721.  
  1722.   if (match_from > num_words) jump Incomplete;
  1723.  
  1724. !  Now we print up the question, using the equivalence classes as worked
  1725. !  out by Adjudicate() so as not to repeat ourselves on plural objects...
  1726.  
  1727.   if (context==6) print "Who"; else print "Which";
  1728.   print " do you mean, ";
  1729.   j=number_of_classes; marker=0;
  1730.   for (i=1:i<=number_of_classes:i++)
  1731.   {   
  1732.       while (((match_classes-->marker) ~= i)
  1733.              && ((match_classes-->marker) ~= -i)) marker++;
  1734.       k=match_list-->marker;
  1735.  
  1736.       if (match_classes-->marker > 0) DefArt(k); else InDefArt(k);
  1737.  
  1738.       if (i<j-1)  print ", ";
  1739.       if (i==j-1) print " or ";
  1740.   }
  1741.   print "?^";
  1742.  
  1743. !  ...and get an answer:
  1744.  
  1745.   .WhichOne;
  1746.   answer_words=Keyboard(buffer2, parse2);
  1747.  
  1748.   first_word=(parse2-->1);
  1749.  
  1750. !  Take care of "all", because that does something too clever here to do
  1751. !  later on:
  1752.  
  1753.   if ((first_word=='all' or 'both' or 'everything')
  1754.       || (first_word=='every' or 'each'))
  1755.   {   
  1756.       if (context>=2 && context<=5)
  1757.       {   l=multiple_object-->0;
  1758.           for (i=0:i<number_matched && l+i<63:i++)
  1759.           {   k=match_list-->i;
  1760.               multiple_object-->(i+1+l) = k;
  1761.           }
  1762.           multiple_object-->0 = i+l;
  1763.           rtrue;
  1764.       }
  1765.       print "Sorry, you can only have one item here.  Which one exactly?^";
  1766.       jump WhichOne;
  1767.   }
  1768.  
  1769. !  If the first word of the reply can be interpreted as a verb, then
  1770. !  assume that the player has ignored the question and given a new
  1771. !  command altogether.
  1772. !  (This is one time when it's convenient that the directions are
  1773. !  not themselves verbs - thus, "north" as a reply to "Which, the north
  1774. !  or south door" is not treated as a fresh command but as an answer.)
  1775.  
  1776.   j=first_word->#dict_par1;
  1777.   if (0~=j&1)
  1778.   {   Copy(buffer, buffer2);
  1779.       Copy(parse, parse2);
  1780.       return 1000;
  1781.   }
  1782.  
  1783. !  Now we insert the answer into the original typed command, as
  1784. !  words additionally describing the same object
  1785. !  (eg, > take red button
  1786. !       Which one, ...
  1787. !       > music
  1788. !  becomes "take music red button".  The parser will thus have three
  1789. !  words to work from next time, not two.)
  1790. !
  1791. !  To do this we use MoveWord which copies in a word.
  1792.  
  1793.   oldw=parse->1;
  1794.   parse->1 = answer_words+oldw;
  1795.  
  1796.   for (k=oldw+answer_words : k>match_from : k--)
  1797.       MoveWord(k, parse, k-answer_words);
  1798.  
  1799.   for (k=1:k<=answer_words:k++)
  1800.       MoveWord(match_from+k-1, parse2, k);
  1801.  
  1802. !  Having reconstructed the input, we warn the parser accordingly
  1803. !  and get out.
  1804.  
  1805.   return 1000;
  1806.  
  1807. !  Now we come to the question asked when the input has run out
  1808. !  and can't easily be guessed (eg, the player typed "take" and there
  1809. !  were plenty of things which might have been meant).
  1810.  
  1811.   .Incomplete;
  1812.  
  1813.   if (context==6) print "Whom"; else print "What";
  1814.   print " do you want";
  1815.   if (actor~=player) { print " "; DefArt(actor); }
  1816.   print " to "; PrintCommand(0,1); print "?^";
  1817.  
  1818.   answer_words=Keyboard(buffer2, parse2);
  1819.  
  1820.   first_word=(parse2-->1);
  1821.  
  1822. !  Once again, if the reply looks like a command, give it to the
  1823. !  parser to get on with and forget about the question...
  1824.  
  1825.   j=first_word->#dict_par1;
  1826.   if (0~=j&1)
  1827.   {   Copy(buffer, buffer2);
  1828.       Copy(parse, parse2);
  1829.       return 1000;
  1830.   }
  1831.  
  1832. !  ...but if we have a genuine answer, then we adjoin the words
  1833. !  typed onto the expression.  But if we've just inferred something
  1834. !  which wasn't actually there, we must adjoin that as well.  (Note
  1835. !  the sneaky use of "it" to match an object inferred this time round.)
  1836.  
  1837.   oldw=parse->1;
  1838.   if (inferfrom==0)
  1839.       for (k=1:k<=answer_words:k++)
  1840.           MoveWord(oldw+k, parse2, k);
  1841.   else
  1842.   {   j=pcount-inferfrom;
  1843.       for (k=1:k<=answer_words:k++)
  1844.           MoveWord(oldw+k+j, parse2, k);
  1845.       for (j=inferfrom:j<pcount:j++)
  1846.       {   if (pattern-->j >= 2 && pattern-->j < 1000)
  1847.           {   parse2-->1 = 'it'; itobj = pattern-->j;
  1848.           }
  1849.           else parse2-->1 = AdjectiveAddress((pattern-->j) - 1000);
  1850.           MoveWord(oldw+1+j-inferfrom, parse2, 1);
  1851.           answer_words++;
  1852.       }
  1853.   }
  1854.   parse->1 = answer_words+oldw;
  1855.  
  1856. !  And go back to the parser.
  1857.   return 1000;
  1858. ];
  1859.  
  1860. ! ----------------------------------------------------------------------------
  1861. !  The Adjudicate routine tries to see if there is an obvious choice, when
  1862. !  faced with a list of objects (the match_list) each of which matches the
  1863. !  player's specification equally well.
  1864. !
  1865. !  To do this it makes use of the context (the token type being worked on).
  1866. !  It counts up the number of obvious choices for the given context
  1867. !  (all to do with where a candidate is, except for 6 (animate) which is to
  1868. !  do with whether it is animate or not);
  1869. !
  1870. !  if only one obvious choice is found, that is returned;
  1871. !
  1872. !  if we are in indefinite mode (don't care which) one of the obvious choices
  1873. !    is returned, or if there is no obvious choice then an unobvious one is
  1874. !    made;
  1875. !
  1876. !  at this stage, we work out whether the objects are distinguishable from
  1877. !    each other or not: if they are all indistinguishable from each other,
  1878. !    then choose one, it doesn't matter which;
  1879. !
  1880. !  otherwise, 0 (meaning, unable to decide) is returned (but remember that
  1881. !    the equivalence classes we've just worked out will be needed by other
  1882. !    routines to clear up this mess, so we can't economise on working them
  1883. !    out).
  1884. !
  1885. !  Returns -1 if an error occurred
  1886. ! ----------------------------------------------------------------------------
  1887.  
  1888. [ Adjudicate context i j k good_ones last n ultimate flag offset;
  1889.  
  1890.   if (parser_trace>=4)
  1891.       print "   [Adjudicating match list of size ", number_matched, "^";
  1892.  
  1893.   j=number_matched-1; good_ones=0; last=match_list-->0;
  1894.   for (i=0:i<=j:i++)
  1895.   {   n=match_list-->i;
  1896.       if (n hasnt concealed)
  1897.       {   ultimate=n;
  1898.           do
  1899.               ultimate=parent(ultimate);
  1900.           until (ultimate==location or actor or 0);
  1901.  
  1902.           if (context==0 && ultimate==location &&
  1903.               (token_was==0 || UserFilter(n)==1)) { good_ones++; last=n; }
  1904.           if (context==1 && parent(n)==actor)     { good_ones++; last=n; }
  1905.           if (context==2 && ultimate==location)   { good_ones++; last=n; }
  1906.           if (context==3 && parent(n)==actor)     { good_ones++; last=n; }
  1907.  
  1908.           if (context==4 or 5)
  1909.           {   if (advance_warning==-1)
  1910.               {   if (parent(n)==actor) { good_ones++; last=n; }
  1911.               }
  1912.               else
  1913.               {   if (context==4 && parent(n)==actor && n~=advance_warning)
  1914.                   { good_ones++; last=n; }
  1915.                   if (context==5 && parent(n)==actor && n in advance_warning)
  1916.                   { good_ones++; last=n; }
  1917.               }
  1918.           }
  1919.           if (context==6 && n has animate)        { good_ones++; last=n; }
  1920.       }
  1921.   }
  1922.   if (good_ones==1) return last;
  1923.  
  1924.   ! If there is ambiguity about what was typed, but it definitely wasn't
  1925.   ! animate as required, then return anything; higher up in the parser
  1926.   ! a suitable error will be given.  (This prevents a question being asked.)
  1927.   !
  1928.   if (context==6 && good_ones==0) return match_list-->0;
  1929.  
  1930.   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  1931.   {   if (context<2 || context>5) { etype=MULTI_PE; return -1; }
  1932.       i=0; number_of_classes=1; offset=multiple_object-->0;
  1933.       for (j=BestGuess():j~=-1 && i<indef_wanted
  1934.            && i+offset<63:j=BestGuess())
  1935.       {   flag=0;
  1936.           if (j hasnt concealed && j hasnt worn) flag=1;
  1937.           if (context==3 or 4 && parent(j)~=actor) flag=0;
  1938.           k=ChooseObjects(j,flag);
  1939.           if (k==1) flag=1; else { if (k==2) flag=0; }
  1940.           if (flag==1)
  1941.           {   i++; multiple_object-->(i+offset) = j;
  1942.               if (parser_trace>=4)
  1943.               print "   Accepting it^";
  1944.           }
  1945.           else
  1946.           {   if (parser_trace>=4)
  1947.                   print "   Rejecting it^";
  1948.           }
  1949.       }
  1950.       if (i<indef_wanted && indef_wanted<100)
  1951.       {   etype=TOOFEW_PE; multi_wanted=indef_wanted;
  1952.           multi_had=multiple_object-->0;
  1953.           return -1;
  1954.       }
  1955.       multiple_object-->0 = i+offset;
  1956.       multi_context=context;
  1957.       if (parser_trace>=4)
  1958.           print "   Made multiple object of size ", i, "]^";
  1959.       return 1;
  1960.   }
  1961.  
  1962.   for (i=0:i<number_matched:i++) match_classes-->i=0;
  1963.  
  1964.   n=1;
  1965.   for (i=0:i<number_matched:i++)
  1966.       if (match_classes-->i==0)
  1967.       {   match_classes-->i=n++; flag=0;
  1968.           for (j=i+1:j<number_matched:j++)
  1969.               if (match_classes-->j==0
  1970.                   && Identical(match_list-->i, match_list-->j)==1)
  1971.               {   flag=1;
  1972.                   match_classes-->j=match_classes-->i;
  1973.               }
  1974.           if (flag==1) match_classes-->i = 1-n;
  1975.       }
  1976.   n--;
  1977.  
  1978.   if (parser_trace>=4)
  1979.   {   print "   Difficult adjudication with ", n, " equivalence classes:^";
  1980.       for (i=0:i<number_matched:i++)
  1981.       {   print "   "; CDefArt(match_list-->i);
  1982.           print " (", match_list-->i, ")  ---  ",match_classes-->i, "^";
  1983.       }
  1984.   }
  1985.  
  1986.   number_of_classes = n;
  1987.  
  1988.   if (n>1 && indef_mode==0)
  1989.   {   j=0; good_ones=0;
  1990.       for (i=0:i<number_matched:i++)
  1991.       {   k=ChooseObjects(match_list-->i,2);
  1992.           if (k==j) good_ones++;
  1993.           if (k>j) { j=k; good_ones=1; last=match_list-->i; }
  1994.       }
  1995.       if (good_ones==1)
  1996.       {   if (parser_trace>=4)
  1997.               print "   ChooseObjects picked a best.]^";
  1998.           return last;
  1999.       }
  2000.       if (parser_trace>=4)
  2001.           print "   Unable to decide: it's a draw.]^";
  2002.       return 0;
  2003.   }
  2004.  
  2005. !  When the player is really vague, or there's a single collection of
  2006. !  indistinguishable objects to choose from, choose the one the player
  2007. !  most recently acquired, or if the player has none of them, then
  2008. !  the one most recently put where it is.
  2009.  
  2010.   if (indef_mode==0) indef_type=0;
  2011.   if (n==1) dont_infer = 1;
  2012.  
  2013.   return BestGuess();
  2014. ];
  2015.  
  2016. ! ----------------------------------------------------------------------------
  2017. !  ReviseMulti  revises the multiple object which already exists, in the
  2018. !    light of information which has come along since then (i.e., the second
  2019. !    parameter).  It returns a parser error number, or else 1 if all is well.
  2020. !    This only ever throws things out, never adds new ones.
  2021. ! ----------------------------------------------------------------------------
  2022.  
  2023. [ ReviseMulti second_p  i low;
  2024.  
  2025.   if (parser_trace>=4)
  2026.       print "   Revising multiple object list of size ", i,
  2027.             " with 2nd ", object second_p, "^";
  2028.  
  2029.   if (multi_context==4 or 5)
  2030.   {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2031.       {   if ( (multi_context==4 && multiple_object-->i ~= second_p)
  2032.                || (multi_context==5 && multiple_object-->i in second_p))
  2033.           {   low++; multiple_object-->low = multiple_object-->i;
  2034.           }
  2035.       }
  2036.       multiple_object-->0 = low;
  2037.   }
  2038.  
  2039.   if (multi_context==2)
  2040.   {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2041.           if (parent(multiple_object-->i)==parent(actor)) low++;
  2042.       if (parser_trace>=4)
  2043.           print "   Token 2 plural case: number with actor ", low, "^";
  2044.       if (action_to_be==##Take || low>0)
  2045.       {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2046.           {   if (parent(multiple_object-->i)==parent(actor))
  2047.               {   low++; multiple_object-->low = multiple_object-->i;
  2048.               }
  2049.           }
  2050.           multiple_object-->0 = low;
  2051.       }
  2052.   }
  2053.  
  2054.   i=multiple_object-->0;
  2055.   if (parser_trace>=4)
  2056.       print "   Done: new size ", i, "^";
  2057.   if (i==0) return NOTHING_PE;
  2058.   return 0;
  2059. ];
  2060.  
  2061. ! ----------------------------------------------------------------------------
  2062. !  ScoreMatchL  scores the match list for quality in terms of what the
  2063. !  player has vaguely asked for.  Points are awarded for conforming with
  2064. !  requirements like "my", and so on.  If the score is less than the
  2065. !  threshold, block out the entry to -1.
  2066. !  The scores are put in the match_classes array, which we can safely
  2067. !  reuse by now.
  2068. ! ----------------------------------------------------------------------------
  2069.  
  2070. [ ScoreMatchL  its_owner its_score obj i threshold a_s l_s;
  2071.  
  2072.   if (indef_type & OTHER_BIT ~= 0) threshold=40;
  2073.   if (indef_type & MY_BIT ~= 0)    threshold=threshold+40;
  2074.   if (indef_type & THAT_BIT ~= 0)  threshold=threshold+40;
  2075.   if (indef_type & ITS_BIT ~= 0)   threshold=threshold+40;
  2076.   if (indef_type & HIS_BIT ~= 0)   threshold=threshold+40;
  2077.   if (indef_type & LIT_BIT ~= 0)   threshold=threshold+40;
  2078.   if (indef_type & UNLIT_BIT ~= 0) threshold=threshold+40;
  2079.  
  2080.   if (parser_trace>=4) print "   Scoring match list with type ", indef_type,
  2081.       ", threshold ", threshold, ":^";
  2082.  
  2083.   a_s = 30; l_s = 20;
  2084.   if (action_to_be == ##Take or ##Remove) { a_s=20; l_s=30; }
  2085.  
  2086.   for (i=0:i<number_matched:i++)
  2087.   {   obj = match_list-->i; its_owner = parent(obj); its_score=0;
  2088.       if (its_owner==actor)   its_score=a_s;
  2089.       if (its_owner==A_Location(actor)) its_score=l_s;
  2090.       if (its_score==0 && its_owner~=compass) its_score=10;
  2091.  
  2092.       if (indef_type & OTHER_BIT ~=0
  2093.           &&  obj~=itobj or himobj or herobj)
  2094.           its_score=its_score+40;
  2095.       if (indef_type & MY_BIT ~=0  &&  its_owner==actor)
  2096.           its_score=its_score+40;
  2097.       if (indef_type & THAT_BIT ~=0  &&  its_owner==A_Location(actor))
  2098.           its_score=its_score+40;
  2099.       if (indef_type & LIT_BIT ~=0  &&  obj has light)
  2100.           its_score=its_score+40;
  2101.       if (indef_type & UNLIT_BIT ~=0  &&  obj hasnt light)
  2102.           its_score=its_score+40;
  2103.       if (indef_type & ITS_BIT ~=0  &&  its_owner==itobj)
  2104.           its_score=its_score+40;
  2105.       if (indef_type & HIS_BIT ~=0  &&  its_owner has animate
  2106.           && GetGender(its_owner)==1)
  2107.           its_score=its_score+40;
  2108.  
  2109.       its_score=its_score + ChooseObjects(obj,2);
  2110.  
  2111.       if (its_score < threshold) match_list-->i=-1;
  2112.       else
  2113.       {   match_classes-->i=its_score;
  2114.           if (parser_trace >= 4)
  2115.           {   print "   "; CDefArt(match_list-->i);
  2116.               print " (", match_list-->i, ") in "; DefArt(its_owner);
  2117.               print " scores ",its_score, "^";
  2118.           }
  2119.       }
  2120.   }
  2121.   number_of_classes=2;
  2122. ];
  2123.  
  2124. [ A_Location a; while(parent(a)~=0) a=parent(a); return a; ];
  2125.  
  2126. ! ----------------------------------------------------------------------------
  2127. !  BestGuess makes the best guess it can out of the match list, assuming that
  2128. !  everything in the match list is textually as good as everything else;
  2129. !  however it ignores items marked as -1, and so marks anything it chooses.
  2130. !  It returns -1 if there are no possible choices.
  2131. ! ----------------------------------------------------------------------------
  2132.  
  2133. [ BestGuess  earliest its_score best i;
  2134.  
  2135.   if (number_of_classes~=1) ScoreMatchL();
  2136.  
  2137.   earliest=0; best=-1;
  2138.   for (i=0:i<number_matched:i++)
  2139.   {   if (match_list-->i >= 0)
  2140.       {   its_score=match_classes-->i;
  2141.           if (its_score>best) { best=its_score; earliest=i; }
  2142.       }
  2143.   }
  2144.   if (parser_trace>=4)
  2145.   {   if (best<0)
  2146.           print "   Best guess ran out of choices^";
  2147.       else
  2148.       {   print "   Best guess "; DefArt(match_list-->earliest);
  2149.           print  " (", match_list-->earliest, ")^";
  2150.       }
  2151.   }
  2152.   if (best<0) return -1;
  2153.   i=match_list-->earliest;
  2154.   match_list-->earliest=-1;
  2155.   return i;
  2156. ];
  2157.  
  2158. ! ----------------------------------------------------------------------------
  2159. !  Identical decides whether or not two objects can be distinguished from
  2160. !  each other by anything the player can type.  If not, it returns true.
  2161. ! ----------------------------------------------------------------------------
  2162.  
  2163. [ Identical o1 o2 p1 p2 n1 n2 i j flag;
  2164.  
  2165. !  print "Id on ", o1, " (", object o1, ") and ", o2, " (", object o2, ")^";
  2166.  
  2167.   if (o1==o2) rtrue;  ! This should never happen, but to be on the safe side
  2168.   if (o1==0 || o2==0) rfalse;  ! Similarly
  2169.   if (parent(o1)==compass || parent(o2)==compass) rfalse; ! Saves time
  2170.  
  2171. !  What complicates things is that o1 or o2 might have a parsing routine,
  2172. !  so the parser can't know from here whether they are or aren't the same.
  2173. !  If they have different parsing routines, we simply assume they're
  2174. !  different.  If they have the same routine (which they probably got from
  2175. !  a class definition) then the decision process is as follows:
  2176. !
  2177. !     the routine is called (with self being o1, not that it matters)
  2178. !       with noun and second being set to o1 and o2, and action being set
  2179. !       to the fake action TheSame.  If it returns -1, they are found
  2180. !       identical; if -2, different; and if >=0, then the usual method
  2181. !       is used instead.
  2182.  
  2183.   if (o1.parse_name~=0 || o2.parse_name~=0)
  2184.   {   if (o1.parse_name ~= o2.parse_name) rfalse;
  2185.       parser_action=##TheSame; parser_one=o1; parser_two=o2;
  2186.       j=wn; i=RunRoutines(o1,parse_name); wn=j;
  2187.       if (i==-1) rtrue; if (i==-2) rfalse;
  2188.   }
  2189.  
  2190. !  This is the default algorithm: do they have the same words in their
  2191. !  "name" (i.e. property no. 1) properties.  (Note that the following allows
  2192. !  for repeated words and words in different orders.)
  2193.  
  2194.   p1 = o1.&1; n1 = (o1.#1)/2;
  2195.   p2 = o2.&1; n2 = (o2.#1)/2;
  2196.  
  2197. !  for (i=0:i<n1:i++) { print_addr p1-->i; print " "; } new_line;
  2198. !  for (i=0:i<n2:i++) { print_addr p2-->i; print " "; } new_line;
  2199.  
  2200.   for (i=0:i<n1:i++)
  2201.   {   flag=0;
  2202.       for (j=0:j<n2:j++)
  2203.           if (p1-->i == p2-->j) flag=1;
  2204.       if (flag==0) rfalse;
  2205.   }
  2206.  
  2207.   for (j=0:j<n2:j++)
  2208.   {   flag=0;
  2209.       for (i=0:i<n1:i++)
  2210.           if (p1-->i == p2-->j) flag=1;
  2211.       if (flag==0) rfalse;
  2212.   }
  2213.  
  2214. !  print "Which are identical!^";
  2215.   rtrue;
  2216. ];
  2217.  
  2218. ! ----------------------------------------------------------------------------
  2219. !  PrintCommand reconstructs the command as it presently reads, from
  2220. !  the pattern which has been built up
  2221. !
  2222. !  If from is 0, it starts with the verb: then it goes through the pattern.
  2223. !  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
  2224. !  if 1, it goes up to pcount-1.
  2225. !
  2226. !  Note that verbs and prepositions are printed out of the dictionary:
  2227. !  and that since the dictionary may only preserve the first six characters
  2228. !  of a word (in a V3 game), we have to hand-code the longer words needed.
  2229. !
  2230. !  (Recall that pattern entries are 0 for "multiple object", 1 for "special
  2231. !  word", 2 to 999 are object numbers and 1000+n means the preposition n)
  2232. ! ----------------------------------------------------------------------------
  2233.  
  2234. [ PrintCommand from emptyf i j k f;
  2235.   if (from==0)
  2236.   {   i=verb_word; from=1; f=1;
  2237. #IFV3;
  2238.       if (i=='inventory') { print "take an inventory"; jump VerbPrinted; }
  2239.       if (i=='examine')   { print "examine";           jump VerbPrinted; }
  2240.       if (i=='discard')   { print "discard";           jump VerbPrinted; }
  2241.       if (i=='swallow')   { print "swallow";           jump VerbPrinted; }
  2242.       if (i=='embrace')   { print "embrace";           jump VerbPrinted; }
  2243.       if (i=='squeeze')   { print "squeeze";           jump VerbPrinted; }
  2244.       if (i=='purchase')  { print "purchase";          jump VerbPrinted; }
  2245.       if (i=='unscrew')   { print "unscrew";           jump VerbPrinted; }
  2246.       if (i=='describe')  { print "describe";          jump VerbPrinted; }
  2247.       if (i=='uncover')   { print "uncover";           jump VerbPrinted; }
  2248.       if (i=='discard')   { print "discard";           jump VerbPrinted; }
  2249.       if (i=='transfer')  { print "transfer";          jump VerbPrinted; }
  2250. #ENDIF;
  2251.       if (i==#n$l)         { print "look";              jump VerbPrinted; }
  2252.       if (i==#n$z)         { print "wait";              jump VerbPrinted; }
  2253.       if (i==#n$x)         { print "examine";           jump VerbPrinted; }
  2254.       if (i==#n$i or 'inv') { print "inventory";        jump VerbPrinted; }
  2255.       if (PrintVerb(i)==0) print_addr i;
  2256.   }
  2257.   .VerbPrinted;
  2258.   j=pcount-emptyf;
  2259.   for (k=from:k<=j:k++)
  2260.   {   if (f==1) print_char ' ';
  2261.       i=pattern-->k;
  2262.       if (i==0) { print "those things"; jump TokenPrinted; }
  2263.       if (i==1) { print "that"; jump TokenPrinted; }
  2264.       if (i>=1000)
  2265.       {   i=AdjectiveAddress(i-1000);
  2266. #IFV3;
  2267.           if (i=='against') { print "against";      jump TokenPrinted; }
  2268. #ENDIF;
  2269.           print_addr i;
  2270.       }
  2271.       else DefArt(i);
  2272.       .TokenPrinted;
  2273.       f=1;
  2274.   }
  2275. ];
  2276.  
  2277. ! ----------------------------------------------------------------------------
  2278. !  The CantSee routine returns a good error number for the situation where
  2279. !  the last word looked at didn't seem to refer to any object in context.
  2280. !
  2281. !  The idea is that: if the actor is in a location (but not inside something
  2282. !  like, for instance, a tank which is in that location) then an attempt to
  2283. !  refer to one of the words listed as meaningful-but-irrelevant there
  2284. !  will cause "you don't need to refer to that in this game" rather than
  2285. !  "no such thing" or "what's 'it'?".
  2286. !  (The advantage of not having looked at "irrelevant" local nouns until now
  2287. !  is that it stops them from clogging up the ambiguity-resolving process.
  2288. !  Thus game objects always triumph over scenery.)
  2289. ! ----------------------------------------------------------------------------
  2290.  
  2291. [ CantSee  i w e;
  2292.     saved_oops=oops_from;
  2293.  
  2294.     if (scope_token~=0) { scope_error = scope_token; return ASKSCOPE_PE; }
  2295.  
  2296.     wn--; w=NextWord();
  2297.     e=CANTSEE_PE;
  2298.     if (w==vague_word) e=ITGONE_PE;
  2299.     i=parent(actor);
  2300.     if (i has visited && Refers(i,w)==1) e=SCENERY_PE;
  2301.     if (etype>e) return etype;
  2302.     return e;
  2303. ];
  2304.  
  2305. ! ----------------------------------------------------------------------------
  2306. !  The MultiAdd routine adds object "o" to the multiple-object-list.
  2307. !
  2308. !  This is only allowed to hold 63 objects at most, at which point it ignores
  2309. !  any new entries (and sets a global flag so that a warning may later be
  2310. !  printed if need be).
  2311. ! ----------------------------------------------------------------------------
  2312.  
  2313. [ MultiAdd o i j;
  2314.   i=multiple_object-->0;
  2315.   if (i==63) { toomany_flag=1; rtrue; }
  2316.   for (j=1:j<=i:j++)
  2317.       if (o==multiple_object-->j) 
  2318.           rtrue;
  2319.   i++;
  2320.   multiple_object-->i = o;
  2321.   multiple_object-->0 = i;
  2322. ];
  2323.  
  2324. ! ----------------------------------------------------------------------------
  2325. !  The MultiSub routine deletes object "o" from the multiple-object-list.
  2326. !
  2327. !  It returns 0 if the object was there in the first place, and 9 (because
  2328. !  this is the appropriate error number in Parser()) if it wasn't.
  2329. ! ----------------------------------------------------------------------------
  2330.  
  2331. [ MultiSub o i j k et;
  2332.   i=multiple_object-->0; et=0;
  2333.   for (j=1:j<=i:j++)
  2334.       if (o==multiple_object-->j)
  2335.       {   for (k=j:k<=i:k++)
  2336.               multiple_object-->k = multiple_object-->(k+1);
  2337.           multiple_object-->0 = --i;
  2338.           return et;
  2339.       }
  2340.   et=9; return et;
  2341. ];
  2342.  
  2343. ! ----------------------------------------------------------------------------
  2344. !  The MultiFilter routine goes through the multiple-object-list and throws
  2345. !  out anything without the given attribute "attr" set.
  2346. ! ----------------------------------------------------------------------------
  2347.  
  2348. [ MultiFilter attr  i j o;
  2349.   .MFiltl;
  2350.   i=multiple_object-->0;
  2351.   for (j=1:j<=i:j++)
  2352.   {   o=multiple_object-->j;
  2353.       if (o hasnt attr) { MultiSub(o); jump Mfiltl; }
  2354.   }
  2355. ];
  2356.  
  2357. ! ----------------------------------------------------------------------------
  2358. !  The UserFilter routine consults the user's filter (or checks on attribute)
  2359. !  to see what already-accepted nouns are acceptable
  2360. ! ----------------------------------------------------------------------------
  2361.  
  2362. [ UserFilter obj;
  2363.  
  2364.   if (token_was>=128)
  2365.   {   if (obj has (token_was-128)) rtrue;
  2366.       rfalse;
  2367.   }
  2368.   noun=obj;
  2369.   return (indirect(#preactions_table-->(token_was-16)));
  2370. ];
  2371.  
  2372. ! ----------------------------------------------------------------------------
  2373. !  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
  2374. !  (the main parse buffer)
  2375. ! ----------------------------------------------------------------------------
  2376.  
  2377. [ MoveWord at1 b2 at2 x y;
  2378.   x=at1*2-1; y=at2*2-1;
  2379.   parse-->x++ = b2-->y++;
  2380.   parse-->x = b2-->y;
  2381. ];
  2382.  
  2383. ! ----------------------------------------------------------------------------
  2384. !  SearchScope  domain1 domain2 context
  2385. !
  2386. !  Works out what objects are in scope (possibly asking an outside routine),
  2387. !  but does not look at anything the player has typed.
  2388. ! ----------------------------------------------------------------------------
  2389.  
  2390. [ SearchScope domain1 domain2 context i;
  2391.  
  2392.   i=0;
  2393. !  Everything is in scope to the debugging commands
  2394.  
  2395. #ifdef DEBUG;
  2396.   if (verb_word == 'purloin' or 'tree' or 'abstract' || verb_word == 'gonear')
  2397.   {   for (i=selfobj+1:i<=top_object:i++) PlaceInScope(i);
  2398.       rtrue;
  2399.   }
  2400. #endif;
  2401.  
  2402. !  First, a scope token gets priority here:
  2403.  
  2404.   if (scope_token ~= 0)
  2405.   {   scope_stage=2;
  2406.       if (indirect(scope_token)~=0) rtrue;
  2407.   }
  2408.  
  2409. !  Next, call any user-supplied routine adding things to the scope,
  2410. !  which may circumvent the usual routines altogether if they return true:
  2411.  
  2412.   if (domain1==actor)
  2413.   {   if (InScope(domain1)~=0) rtrue;
  2414.   }
  2415.   else if (domain2==actor)
  2416.   {   if (InScope(domain2)~=0) rtrue;
  2417.   }
  2418.  
  2419. !  Pick up everything in the location except the actor's possessions;
  2420. !  then go through those.  (This ensures the actor's possessions are in
  2421. !  scope even in Darkness.)
  2422.  
  2423.   if (context==5 && advance_warning ~= -1)       !  Scope for multiinside
  2424.   {   ScopeWithin(advance_warning, 0, context);  !  is different
  2425.   }
  2426.   else
  2427.   {   ScopeWithin(domain1, domain2, context);
  2428.       ScopeWithin(domain2,0,context);
  2429.   }
  2430. ];
  2431.  
  2432. ! ----------------------------------------------------------------------------
  2433. !  PlaceInScope is provided for routines outside the library, and is not
  2434. !  called within the parser (except for debugging purposes).
  2435. ! ----------------------------------------------------------------------------
  2436.  
  2437. [ PlaceInScope thing;
  2438.  
  2439.    if (et_flag==1) { DoEachTurn(thing); rtrue; }
  2440.    wn=match_from; TryGivenObject(thing); placed_in_flag=1;
  2441. ];
  2442.  
  2443. ! ----------------------------------------------------------------------------
  2444. !  DoEachTurn
  2445. ! ----------------------------------------------------------------------------
  2446.  
  2447. [ DoEachTurn thing;
  2448.   if (parser_trace>=5)
  2449.   {   print "[Considering each_turn for "; DefArt(thing); print "]^"; }
  2450.   if (thing.each_turn~=NULL) PrintOrRun(thing, each_turn);
  2451. ];
  2452.  
  2453. ! ----------------------------------------------------------------------------
  2454. !  ScopeWithin looks for objects in the domain which make textual sense
  2455. !  and puts them in the match list.  (However, it does not recurse through
  2456. !  the second argument.)
  2457. ! ----------------------------------------------------------------------------
  2458.  
  2459. [ ScopeWithin domain nosearch context;
  2460.  
  2461.    if (domain==0) rtrue;
  2462.  
  2463. !  multiexcept doesn't have second parameter in scope
  2464.    if (context==4 && domain==advance_warning) rtrue;
  2465.  
  2466. !  Special rule: the directions (interpreted as the 12 walls of a room) are
  2467. !  always in context.  (So, e.g., "examine north wall" is always legal.)
  2468. !  (Unless we're parsing something like "all", because it would just slow
  2469. !  things down then, or unless the context is "creature".)
  2470.  
  2471.    if (indef_mode==0 && domain==location && et_flag==0
  2472.        && context~=6) ScopeWithin(compass);
  2473.  
  2474. !  Look through the objects in the domain
  2475.  
  2476.    objectloop (domain in domain) ScopeWithin_O(domain, nosearch, context);
  2477. ];
  2478.  
  2479. [ ScopeWithin_O domain nosearch context i ad n;
  2480.  
  2481. !  In each_turn mode, we're going through calling e_t for everything in
  2482. !  scope and not doing any parsing at all:
  2483.  
  2484.       if (et_flag==1) { DoEachTurn(domain); jump DontAccept; }
  2485.  
  2486. !  If we're beyond the end of the user's typing, accept everything
  2487. !  (NounDomain will sort things out)
  2488.  
  2489.       if (match_from > num_words)
  2490.       {   i=parser_trace; parser_trace=0;
  2491.           if (i>=5) { print "     Out of text: matching "; DefArt(domain);
  2492.                       new_line; }
  2493.           MakeMatch(domain,1);
  2494.           parser_trace=i; jump DontAccept;
  2495.       }
  2496.  
  2497. !  "it" or "them" matches to the it-object only.  (Note that (1) this means
  2498. !  that "it" will only be understood if the object in question is still
  2499. !  in context, and (2) only one match can ever be made in this case.)
  2500.  
  2501.       wn=match_from;
  2502.       i=NounWord();
  2503.       if (i==1 && itobj==domain)   MakeMatch(itobj,1);
  2504.       if (i==2 && himobj==domain)  MakeMatch(himobj,1);
  2505.       if (i==3 && herobj==domain)  MakeMatch(herobj,1);
  2506.       if (i==4 && player==domain)  MakeMatch(player,1);
  2507.  
  2508. !  Construing the current word as the start of a noun, can it refer to the
  2509. !  object?
  2510.  
  2511.       wn--; TryGivenObject(domain);
  2512.  
  2513.       .DontAccept;
  2514.  
  2515. !  Shall we consider the possessions of the current object, as well?
  2516. !  Only if it's a container (so, for instance, if a dwarf carries a
  2517. !  sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
  2518. !  will).
  2519. !  Also, only if there are such possessions.
  2520. !
  2521. !  Notice that the parser can see "into" anything flagged as
  2522. !  transparent - such as a dwarf whose sword you can get at.
  2523.  
  2524.       if (child(domain)~=0
  2525.           && domain ~= nosearch
  2526.           && (domain has supporter
  2527.               || domain has transparent
  2528.               || (domain has container && domain has open)))
  2529.           ScopeWithin(domain,0,context);
  2530.  
  2531. !  Drag any extras into context
  2532.  
  2533.    ad = domain.&add_to_scope;
  2534.    if (ad ~= 0)
  2535.    {   if (UnsignedCompare(ad-->0,top_object) > 0)
  2536.        {   ats_flag = 2+context;
  2537.            RunRoutines(domain, add_to_scope);
  2538.            ats_flag = 0;
  2539.        }
  2540.        else
  2541.        {   n=domain.#add_to_scope;
  2542.            for (i=0:(2*i)<n:i++)
  2543.                ScopeWithin_O(ad-->i,0,context);
  2544.        }
  2545.    }
  2546. ];
  2547.  
  2548. [ AddToScope obj;
  2549.    if (ats_flag>=2)
  2550.        ScopeWithin_O(obj,0,ats_flag-2);
  2551.    if (ats_flag==1)
  2552.    {   if  (HasLightSource(obj)==1) ats_hls = 1;
  2553.    }
  2554. ];
  2555.  
  2556. ! ----------------------------------------------------------------------------
  2557. !  MakeMatch looks at how good a match is.  If it's the best so far, then
  2558. !  wipe out all the previous matches and start a new list with this one.
  2559. !  If it's only as good as the best so far, add it to the list.
  2560. !  If it's worse, ignore it altogether.
  2561. !
  2562. !  The idea is that "red panic button" is better than "red button" or "panic".
  2563. !
  2564. !  number_matched (the number of words matched) is set to the current level
  2565. !  of quality.
  2566. !
  2567. !  We never match anything twice, and keep at most 64 equally good items.
  2568. ! ----------------------------------------------------------------------------
  2569.  
  2570. [ MakeMatch obj quality i;
  2571.    if (parser_trace>=5) print "    Match with quality ",quality,"^";
  2572.    if (token_was~=0 && UserFilter(obj)==0)
  2573.    {   if (parser_trace>=5) print "    Match filtered out^";
  2574.        rtrue;
  2575.    }
  2576.    if (quality < match_length) rtrue;
  2577.    if (quality > match_length) { match_length=quality; number_matched=0; }
  2578.    else
  2579.    {   if (2*number_matched>=MATCH_LIST_SIZE) rtrue;
  2580.        for (i=0:i<number_matched:i++)
  2581.            if (match_list-->i==obj) rtrue;
  2582.    }
  2583.    match_list-->number_matched++ = obj;
  2584.    if (parser_trace>=5) print "    Match added to list^";
  2585. ];
  2586.  
  2587. ! ----------------------------------------------------------------------------
  2588. !  TryGivenObject tries to match as many words as possible in what has been
  2589. !  typed to the given object, obj.  If it manages any words matched at all,
  2590. !  it calls MakeMatch to say so.  There is no return value.
  2591. ! ----------------------------------------------------------------------------
  2592.  
  2593. [ TryGivenObject obj threshold k;
  2594.  
  2595.    if (parser_trace>=5)
  2596.    {   print "    Trying "; DefArt(obj);
  2597.        print " (", obj, ") at word ", wn, "^";
  2598.    }
  2599.  
  2600. !  If input has run out and we're in indefinite mode, then always match,
  2601. !  with only quality 0 (this saves time).
  2602.  
  2603.    if (indef_mode ~=0 && wn > parse->1) { MakeMatch(obj,0); rfalse; }
  2604.  
  2605. !  Ask the object to parse itself if necessary, sitting up and taking notice
  2606. !  if it says the plural was used:
  2607.  
  2608.    if (obj.parse_name~=0)
  2609.    {   parser_action=-1;
  2610.        k=RunRoutines(obj,parse_name);
  2611.        if (k>0)
  2612.        {   if (parser_action == ##PluralFound)
  2613.            {   if (allow_plurals == 0) jump NoWordsMatch;
  2614.                if (indef_mode==0)
  2615.                {   indef_mode=1; indef_type=0; indef_wanted=0; }
  2616.                indef_type=indef_type | PLURAL_BIT;
  2617.                if (indef_wanted==0) indef_wanted=100;
  2618.            }
  2619.            MakeMatch(obj,k); rfalse;
  2620.        }
  2621.        if (k==0) jump NoWordsMatch;
  2622.    }
  2623.  
  2624. !  The default algorithm is simply to count up how many words pass the
  2625. !  Refers test:
  2626.  
  2627.    if (0 == Refers(obj,NounWord()))
  2628.    {   .NoWordsMatch;
  2629.        if (indef_mode~=0) MakeMatch(obj,0);
  2630.        rfalse;
  2631.    }
  2632.  
  2633.    threshold=1;
  2634.  
  2635.    while (0~=Refers(obj,NextWord())) threshold++;
  2636.    MakeMatch(obj,threshold);
  2637.  
  2638.    if (parser_trace>=5) print "    Matched^";
  2639. ];
  2640.  
  2641. ! ----------------------------------------------------------------------------
  2642. !  Refers works out whether the word with dictionary address wd can refer to
  2643. !  the object obj, by seeing if wd is listed in the "names" property of obj.
  2644. ! ----------------------------------------------------------------------------
  2645.  
  2646. [ Refers obj wd   k l m;
  2647.     if (obj==0) rfalse;
  2648.     k=obj.&1; l=(obj.#1)/2-1;
  2649.     for (m=0:m<=l:m++)
  2650.         if (wd==k-->m) rtrue;
  2651.     rfalse;
  2652. ];
  2653.  
  2654. ! ----------------------------------------------------------------------------
  2655. !  NounWord (which takes no arguments) returns:
  2656. !
  2657. !   1  if the next word is "it" or "them",
  2658. !   2  if the next word is "him",
  2659. !   3  if the next word is "her",
  2660. !   4  if "me", "myself", "self"
  2661. !   0  if the next word is unrecognised or does not carry the "noun" bit in
  2662. !      its dictionary entry,
  2663. !   or the address in the dictionary if it is a recognised noun.
  2664. !
  2665. !  The "current word" marker moves on one.
  2666. ! ----------------------------------------------------------------------------
  2667.  
  2668. [ NounWord i;
  2669.    i=NextWord();
  2670.    if (i=='it' or 'them') return 1;
  2671.    if (i=='him') return 2;
  2672.    if (i=='her') return 3;
  2673.    if (i=='me' or 'myself' or 'self') return 4;
  2674.    if (i==0) rfalse;
  2675.    if ((i->#dict_par1)&128 == 0) rfalse;
  2676.    return i;
  2677. ];
  2678.  
  2679. ! ----------------------------------------------------------------------------
  2680. !  Adjective (which takes no arguments) returns:
  2681. !
  2682. !   0  if the next word is listed in the dictionary as possibly an adjective,
  2683. !   or its adjective number if it is.
  2684. !
  2685. !  The "current word" marker moves on one.
  2686. ! ----------------------------------------------------------------------------
  2687.  
  2688. [ Adjective i j;
  2689.    j=NextWord();
  2690.    if (j==0) rfalse;
  2691.    i=j->#dict_par1;
  2692.    if (i&8 == 0) rfalse;
  2693.    return(j->#dict_par3);
  2694. ];
  2695.  
  2696. ! ----------------------------------------------------------------------------
  2697. !  AdjectiveAddress works out the address in the dictionary of the word
  2698. !  corresponding to the given adjective number.
  2699. !
  2700. !  It should never produce the given error (which would mean that Inform
  2701. !  had set up the adjectives table incorrectly).
  2702. ! ----------------------------------------------------------------------------
  2703.  
  2704. [ AdjectiveAddress number m;
  2705.    m=#adjectives_table;
  2706.    for (::)
  2707.    {   if (number==m-->1) return m-->0;
  2708.        m=m+4;
  2709.    }
  2710.    m=#adjectives_table;
  2711.    print "<Adjective not found>";
  2712.    return m;
  2713. ];
  2714.  
  2715. ! ----------------------------------------------------------------------------
  2716. !  NextWord (which takes no arguments) returns:
  2717. !
  2718. !  0            if the next word is unrecognised,
  2719. !  comma_word   if it is a comma character
  2720. !               (which is treated oddly by the Z-machine, hence the code)
  2721. !  or the dictionary address if it is recognised.
  2722. !  The "current word" marker is moved on.
  2723. !
  2724. !  NextWordStopped does the same, but returns -1 when input has run out
  2725. ! ----------------------------------------------------------------------------
  2726.  
  2727. [ NextWord i j k;
  2728.    if (wn > parse->1) { wn++; rfalse; }
  2729.    i=wn*2-1; wn++;
  2730.    j=parse-->i;
  2731.    if (j==0)
  2732.    {   k=wn*4-3; i=buffer->(parse->k);
  2733.        if (i==',') j=comma_word;
  2734.        if (i=='.') j='then';
  2735.    }
  2736.    return j;
  2737. ];   
  2738.  
  2739. [ NextWordStopped;
  2740.    if (wn > parse->1) { wn++; return -1; }
  2741.    return NextWord();
  2742. ];
  2743.  
  2744. ! ----------------------------------------------------------------------------
  2745. !  TryNumber is the only routine which really does any character-level
  2746. !  parsing, since that's normally left to the Z-machine.
  2747. !  It takes word number "wordnum" and tries to parse it as an (unsigned)
  2748. !  decimal number, returning
  2749. !
  2750. !  -1000                if it is not a number
  2751. !  the number           if it has between 1 and 4 digits
  2752. !  10000                if it has 5 or more digits.
  2753. !
  2754. !  (The danger of allowing 5 digits is that Z-machine integers are only
  2755. !  16 bits long, and anyway this isn't meant to be perfect.)
  2756. !
  2757. !  Using NumberWord, it also catches "one" up to "twenty".
  2758. !
  2759. !  Note that a game can provide a ParseNumber routine which takes priority,
  2760. !  to enable parsing of odder numbers ("x45y12", say).
  2761. ! ----------------------------------------------------------------------------
  2762.  
  2763. [ TryNumber wordnum   i j c num len mul tot d digit;
  2764.  
  2765.    i=wn; wn=wordnum; j=NextWord(); wn=i;
  2766.    j=NumberWord(j); if (j>=1) return j;
  2767.  
  2768.    i=wordnum*4+1; j=parse->i; num=j+buffer; len=parse->(i-1);
  2769.  
  2770.    tot=ParseNumber(num, len);  if (tot~=0) return tot;
  2771.  
  2772.    if (len>=4) mul=1000;
  2773.    if (len==3) mul=100;
  2774.    if (len==2) mul=10;
  2775.    if (len==1) mul=1;
  2776.  
  2777.    tot=0; c=0; len=len-1;
  2778.  
  2779.    for (c=0:c<=len:c++)
  2780.    {   digit=num->c;
  2781.        if (digit=='0') { d=0; jump digok; }
  2782.        if (digit=='1') { d=1; jump digok; }
  2783.        if (digit=='2') { d=2; jump digok; }
  2784.        if (digit=='3') { d=3; jump digok; }
  2785.        if (digit=='4') { d=4; jump digok; }
  2786.        if (digit=='5') { d=5; jump digok; }
  2787.        if (digit=='6') { d=6; jump digok; }
  2788.        if (digit=='7') { d=7; jump digok; }
  2789.        if (digit=='8') { d=8; jump digok; }
  2790.        if (digit=='9') { d=9; jump digok; }
  2791.        return -1000;
  2792.      .digok;
  2793.        tot=tot+mul*d; mul=mul/10;
  2794.    }
  2795.    if (len>3) tot=10000;
  2796.    return tot;
  2797. ];
  2798.  
  2799. ! ----------------------------------------------------------------------------
  2800. !  ResetVagueWords does, assuming that i was the object last referred to
  2801. ! ----------------------------------------------------------------------------
  2802.  
  2803. [ ResetVagueWords i;
  2804.    if (i has animate && i~=player)
  2805.    {   if (GetGender(i)==1) himobj=i;
  2806.        else herobj=i;
  2807.    }
  2808.    else itobj=i;
  2809. ];
  2810.  
  2811. ! ----------------------------------------------------------------------------
  2812. !  GetGender returns 0 if the given animate object is female, and 1 if male
  2813. !  (not all games will want such a simple decision function!)
  2814. ! ----------------------------------------------------------------------------
  2815.  
  2816. [ GetGender person;
  2817.    if (person hasnt female) rtrue;
  2818.    rfalse;
  2819. ];
  2820.  
  2821. ! ----------------------------------------------------------------------------
  2822. !  For copying buffers
  2823. ! ----------------------------------------------------------------------------
  2824.  
  2825. [ Copy bto bfrom i size;
  2826.    size=bto->0;
  2827.    for (i=1:i<=size:i++) bto->i=bfrom->i;
  2828. ];
  2829.  
  2830. ! ----------------------------------------------------------------------------
  2831. !  Useful routine: unsigned comparison (for addresses in Z-machine)
  2832. !    Returns 1 if x>y, 0 if x=y, -1 if x<y
  2833. !  ZRegion(addr) returns 1 if object num, 2 if in code area, 3 if in strings
  2834. ! ----------------------------------------------------------------------------
  2835.  
  2836. [ UnsignedCompare x y u v;
  2837.   if (x==y) return 0;
  2838.   if (x<0 && y>=0) return 1;
  2839.   if (x>=0 && y<0) return -1;
  2840.   u = x&$7fff; v= y&$7fff;
  2841.   if (u>v) return 1;
  2842.   return -1;
  2843. ];
  2844.  
  2845. [ ZRegion addr;
  2846.   if (addr==0) return 0;
  2847.   if (addr>=1 && addr<=top_object) return 1;
  2848.   if (UnsignedCompare(addr, #strings_offset)>=0) return 3;
  2849.   if (UnsignedCompare(addr, #code_offset)>=0) return 2;
  2850.   return 0;
  2851. ];
  2852.  
  2853. [ PrintOrRun obj prop flag a;
  2854.   if (obj.#prop > 2) return RunRoutines(obj,prop);
  2855.   if (obj.prop==NULL) rfalse;
  2856.   a=ZRegion(obj.prop);
  2857.   if (a==0 or 1) "** Expected string/routine as prop value! **";
  2858.   if (a==3) { print_paddr obj.prop; if (flag==0) new_line; rtrue; }
  2859.   return RunRoutines(obj,prop);
  2860. ];
  2861.  
  2862. [ ValueOrRun obj prop a;
  2863.   a=ZRegion(obj.prop);
  2864.   if (a==2) return RunRoutines(obj,prop);
  2865.   return obj.prop;
  2866. ];
  2867.  
  2868. [ RunRoutines obj prop i j k l m ssv;
  2869.  
  2870.    if (obj==thedark) obj=real_location;
  2871.    if (obj.prop==NULL) rfalse;
  2872.  
  2873. #IFDEF DEBUG;
  2874.  if (debug_flag & 1 ~= 0 && prop~=short_name)
  2875.  { print "[Running ";
  2876.    if (prop==before)   { print "before";   jump DebugPrt; }
  2877.    if (prop==after)    { print "after";    jump DebugPrt; }
  2878.    if (prop==life)     { print "life";     jump DebugPrt; }
  2879.    if (prop==each_turn) { print "each_turn"; jump DebugPrt; }
  2880.    if (prop==describe) { print "describe"; jump DebugPrt; }
  2881.    if (prop==initial)  { print "initial";  jump DebugPrt; }
  2882.    if (prop==n_to)     { print "n_to/door_to";   jump DebugPrt; }
  2883.    if (prop==s_to)     { print "s_to";   jump DebugPrt; }
  2884.    if (prop==e_to)     { print "e_to";   jump DebugPrt; }
  2885.    if (prop==w_to)     { print "w_to/door_dir";   jump DebugPrt; }
  2886.    if (prop==ne_to)    { print "ne_to";   jump DebugPrt; }
  2887.    if (prop==nw_to)    { print "nw_to";   jump DebugPrt; }
  2888.    if (prop==se_to)    { print "se_to/add_to_scope";   jump DebugPrt; }
  2889.    if (prop==sw_to)    { print "sw_to";   jump DebugPrt; }
  2890.    if (prop==u_to)     { print "u_to/invent";   jump DebugPrt; }
  2891.    if (prop==d_to)     { print "d_to/plural";   jump DebugPrt; }
  2892.    if (prop==in_to)    { print "in_to";   jump DebugPrt; }
  2893.    if (prop==out_to)   { print "out_to";   jump DebugPrt; }
  2894.    if (prop==time_out) { print "daemon/time_out";   jump DebugPrt; }
  2895.    if (prop==parse_name) { print "parse_name";   jump DebugPrt; }
  2896.    print "property ",prop;
  2897.    .DebugPrt;
  2898.    print " for ", object obj,"]^";
  2899.  }
  2900. #ENDIF;
  2901.  
  2902.    j=obj.∝ k=obj.#prop; m=self; self=obj;
  2903.    noun=inp1; second=inp2;
  2904.    ssv=sw__var;
  2905.    if (prop==life) sw__var=reason_code;
  2906.    else sw__var=action;
  2907.    for (i=0:i<k/2:i++)
  2908.    {   if (j-->i == NULL) { self=m; sw__var=ssv; rfalse; }
  2909.        l=ZRegion(j-->i);
  2910.        if (l==2)
  2911.        {   l=indirect(j-->i);
  2912.            if (l~=0) { self=m; sw__var=ssv; return l; }
  2913.        }
  2914.        else
  2915.        {   if (l==3) { print_paddr j-->i; new_line; }
  2916.            else print "** Entry in property list not routine or string **^";
  2917.        }
  2918.    }
  2919.    self=m; sw__var=ssv;
  2920.    rfalse;
  2921. ];
  2922.  
  2923. ! ----------------------------------------------------------------------------
  2924. !  End of the parser proper: the remaining routines are its front end.
  2925. ! ----------------------------------------------------------------------------
  2926.  
  2927. [ DisplayStatus;
  2928.    if (the_time==NULL)
  2929.    {   sline1=score; sline2=turns; }
  2930.    else
  2931.    {   sline1=the_time/60; sline2=the_time%60; }
  2932. ];
  2933.  
  2934. [ SetTime t s;
  2935.    the_time=t; time_rate=s; time_step=0;
  2936.    if (s<0) time_step=0-s;
  2937. ];
  2938.  
  2939. [ NotifyTheScore i;
  2940.   print "^[Your score has just gone ";
  2941.   if (last_score > score) { i=last_score-score; print "down"; }
  2942.   else { i=score-last_score; print "up"; }
  2943.   print " by "; EnglishNumber(i); print " point";
  2944.   if (i>1) print "s"; print ".]^";
  2945. ];
  2946.  
  2947. [ PlayTheGame i j k l;
  2948.  
  2949.    top_object = #largest_object-255;
  2950.  
  2951.    j=Initialise();
  2952.  
  2953.    last_score = score;
  2954.    move player to location;
  2955.    while (parent(location)~=0) location=parent(location);
  2956.    objectloop (i in player) give i moved ~concealed;
  2957.    player.capacity = MAX_CARRIED;
  2958.  
  2959.    if (j~=2) Banner();
  2960.  
  2961.    lightflag=OffersLight(parent(player));
  2962.    if (lightflag==0)
  2963.    {   real_location=location; location=thedark;
  2964.    }
  2965.  
  2966.    <Look>;
  2967.  
  2968.    for (i=1:i<=100:i++) j=random(i);
  2969.  
  2970.    while deadflag==0
  2971.    {   if (score ~= last_score)
  2972.        {   if (notify_mode==1) NotifyTheScore();
  2973.            last_score=score;
  2974.        }
  2975.        .Error;
  2976.        inp1=0; inp2=0; action=0; meta=0;
  2977.        inputobjs-->0 = 0; inputobjs-->1 = 0;
  2978.        inputobjs-->2 = 0; inputobjs-->3 = 0;
  2979.        Parser(inputobjs);
  2980.  
  2981.        onotheld_mode=notheld_mode; notheld_mode=0;
  2982.  
  2983.        if (actor~=player)
  2984.        {   action=inputobjs-->0;
  2985.            inp1=inputobjs-->2;
  2986.            inp2=inputobjs-->3;
  2987.            if (action==##GiveR)
  2988.            {   inp2=inputobjs-->2;
  2989.                inp1=inputobjs-->3; action=##Give;
  2990.            }
  2991.            if (action==##ShowR)
  2992.            {   inp2=inputobjs-->2;
  2993.                inp1=inputobjs-->3; action=##Show;
  2994.            }
  2995.            if (action==##Tell && inp1==player)
  2996.            {   noun=actor; actor=player; AskSub(); jump timeslice;
  2997.            }
  2998.            if (RunLife(actor,##Order)==0) L__M(##Order,1,actor);
  2999.            jump timeslice;
  3000.        }
  3001.  
  3002.        if (toomany_flag==1)
  3003.        {   toomany_flag=0; L__M(##Miscellany,1); }
  3004.        if (action==0)
  3005.        {   action=inputobjs-->0;
  3006.            i=inputobjs-->1;
  3007.            inp1=inputobjs-->2;
  3008.            inp2=inputobjs-->3;
  3009.        }
  3010.        else i=2;
  3011.  
  3012.        if (i==0) { inp1=0; inp2=0; }
  3013.        if (i==1) { inp2=0; }
  3014.  
  3015.        multiflag=0;
  3016.        if (i==0) Process(0,0,action);
  3017.        else
  3018.        {   if (inp1~=0) Process(inp1,inp2,action);
  3019.            else
  3020.            {   multiflag=1;
  3021.                j=multiple_object-->0;
  3022.                if (j==0) { L__M(##Miscellany,2); jump Error; }
  3023.                i=location;
  3024.                for (k=1:k<=j:k++)
  3025.                {   if (location~=i)
  3026.                    {   print "(Since something dramatic has happened, \
  3027.                               your list of commands has been cut short.)^";
  3028.                        break;
  3029.                    }
  3030.                    l=multiple_object-->k; PrintShortName(l); print ": ";
  3031.                    Process(l,inp2,action);
  3032.                }
  3033.            }
  3034.        }
  3035.  
  3036.        .timeslice;
  3037.        if (notheld_mode==1) meta=1;
  3038.        if (deadflag==0 && meta==0) Time();
  3039.    }
  3040.  
  3041.    if (deadflag~=2) AfterLife();
  3042.    if (deadflag==0) jump Error;
  3043.  
  3044.    print "^^    ";
  3045.    #IFV5; style bold; #ENDIF;
  3046.    print "***";
  3047.    if (deadflag==1) L__M(##Miscellany,3);
  3048.    if (deadflag==2) L__M(##Miscellany,4);
  3049.    if (deadflag>2)  { print " "; DeathMessage(); print " "; }
  3050.    print "***";
  3051.    #IFV5; style roman; #ENDIF;
  3052.    print "^^^";
  3053.    ScoreSub();
  3054.    DisplayStatus();
  3055.  
  3056.    .RRQPL;
  3057.    L__M(##Miscellany,5);
  3058.    .RRQL;
  3059.    print "> ";
  3060.    #IFV3; read buffer parse; #ENDIF;
  3061.    temp_global=0;
  3062.    #IFV5; read buffer parse DrawStatusLine; #ENDIF;
  3063.    i=parse-->1;
  3064.    if (i=='quit' or #n$q) quit;
  3065.    if (i=='restart')      @restart;
  3066.    if (i=='restore')      { RestoreSub(); jump RRQPL; }
  3067.    if (i=='fullscore' or 'full' && TASKS_PROVIDED==0)
  3068.    {   new_line; FullScoreSub(); jump RRQPL; }
  3069.    if (deadflag==2 && i=='amusing' && AMUSING_PROVIDED==0)
  3070.    {   new_line; Amusing(); jump RRQPL; }
  3071. #IFV5;
  3072.    if (i=='undo')
  3073.    {   if (undo_flag==0)
  3074.        {   L__M(##Miscellany,6);
  3075.            jump RRQPL;
  3076.        }
  3077.        if (undo_flag==1) jump UndoFailed2;
  3078.        @restore_undo i;
  3079.        if (i==0)
  3080.        {   .UndoFailed2; L__M(##Miscellany,7);
  3081.        }
  3082.        jump RRQPL;
  3083.    }
  3084. #ENDIF;
  3085.    L__M(##Miscellany,8);
  3086.    jump RRQL;
  3087. ];
  3088.  
  3089. #ifdef DEBUG;
  3090. [ TraceAction source;
  3091.   print "[Action ", action;
  3092.   if (noun~=0)   { print " with noun "; DefArt(noun);  }
  3093.   if (second~=0) { print " and second "; DefArt(second); }
  3094.   if (source==1) print " (from outside)"; else print " (from parser)";
  3095.   print "]^";
  3096. ];
  3097. #endif;
  3098.  
  3099. [ BeforeRoutines;
  3100.   if (GamePreRoutine()~=0) rtrue;
  3101.   if (RunRoutines(player,before)~=0) rtrue;
  3102.   if (location~=0 && RunRoutines(location,before)~=0) rtrue;
  3103.   if (inp1>1 && RunRoutines(inp1,before)~=0) rtrue;
  3104.   rfalse;
  3105. ];
  3106.  
  3107. [ R_Process acti i j sn ss sa sse;
  3108.    sn=inp1; ss=inp2; sa=action; sse=self;
  3109.    inp1 = i; inp2 = j; noun=i; second=j; action=acti;
  3110.  
  3111. #IFDEF DEBUG;
  3112.    if (debug_flag & 2 ~= 0) TraceAction(1);
  3113. #ENDIF;
  3114.  
  3115.    if ((meta==1 || BeforeRoutines()==0) && action<256)
  3116.    {   indirect(#actions_table-->action);
  3117.        self=sse; inp1=sn; noun=sn; inp2=ss; second=ss; action=sa; rfalse;
  3118.    }
  3119.    self=sse; inp1=sn; noun=sn; inp2=ss; second=ss; action=sa; rtrue;
  3120. ];
  3121.  
  3122. [ Process i j acti;
  3123.    inp1 = i; inp2 = j; noun=i; second=j; action=acti;
  3124.    if (inp1==1) noun=special_number;
  3125.    if (inp2==1)
  3126.    {   if (inp1==1) second=special_number2;
  3127.        else second=special_number;
  3128.    }
  3129. #IFDEF DEBUG;
  3130.    if (debug_flag & 2 ~= 0) TraceAction(0);
  3131. #ENDIF;
  3132.    if (meta==1 || BeforeRoutines()==0)
  3133.        indirect(#actions_table-->action);
  3134. ];
  3135.  
  3136. [ RunLife a j;
  3137.    reason_code = j; return RunRoutines(a,life);
  3138. ];
  3139.  
  3140. [ AfterRoutines;
  3141.  
  3142.    if (RunRoutines(player,after)~=0) rtrue;
  3143.    if (location~=0 && RunRoutines(location,after)~=0) rtrue;
  3144.    if (inp1>1 && RunRoutines(inp1,after)~=0) rtrue;
  3145.  
  3146.    return GamePostRoutine();
  3147. ];
  3148.  
  3149. [ LAfterRoutines;
  3150.    if (location~=0 && RunRoutines(location,after)~=0) rtrue;
  3151.    return GamePostRoutine();
  3152. ];
  3153.  
  3154. [ Banner i;
  3155. #IFV5; style bold; #ENDIF;
  3156.    print_paddr #Story;
  3157. #IFV5; style roman; #ENDIF;
  3158.    print_paddr #Headline;
  3159.    print "Release ", (0-->1) & $03ff, " / Serial number ";
  3160.    for (i=18:i<24:i++) print_char 0->i;
  3161.    print " / Inform v"; inversion;
  3162.    print " Library "; print_paddr #LibRelease;
  3163. #ifdef DEBUG;
  3164.    print " D";
  3165. #endif;
  3166.    new_line;
  3167. ];
  3168.  
  3169. [ VersionSub;
  3170.   Banner();
  3171. #IFV5;
  3172.   print "Interpreter ", 0->$1e, " Version ", char 0->$1f, " / ";
  3173. #ENDIF;
  3174.   print "Library serial number "; print_paddr #LibSerial; new_line;
  3175. ];
  3176.  
  3177. #IFV5;
  3178. Global pretty_flag=1;
  3179. #ENDIF;
  3180. Global item_width=8;
  3181. Global item_name="Nameless item";
  3182. Global menu_item=0;
  3183. ! Global menu_choices="";
  3184.  
  3185. [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i;
  3186.  
  3187.   menu_item=0;
  3188.   lines=indirect(EntryR);
  3189.   main_title=item_name;
  3190.  
  3191.   print "--- "; print_paddr main_title; print " ---^^";
  3192.   print_paddr menu_choices;
  3193.  
  3194.    .LKML;
  3195.   print "^Type a number from 1 to ", lines, " or press ENTER.^> ";
  3196.  
  3197.    #IFV3; read buffer parse; #ENDIF;
  3198.    temp_global=0;
  3199.    #IFV5; read buffer parse DrawStatusLine; #ENDIF;
  3200.    i=parse-->1;
  3201.    if (i=='quit' or #n$q || parse->1==0)
  3202.    {   if (deadflag==0) <<Look>>;
  3203.        rfalse;
  3204.    }
  3205.    i=TryNumber(1);
  3206.    if (i<1 || i>lines) jump LKML;
  3207.    menu_item=i;
  3208.    indirect(ChoiceR);
  3209.    jump LKML;
  3210. ];
  3211.  
  3212. #IFV3;
  3213. [ DoMenu menu_choices EntryR ChoiceR;
  3214.   LowKey_Menu(menu_choices,EntryR,ChoiceR);
  3215. ];
  3216. #ENDIF;
  3217.  
  3218. #IFV5;
  3219. Global menu_nesting = 0;
  3220. [ DoMenu menu_choices EntryR ChoiceR
  3221.          lines main_title main_wid cl i j oldcl pkey;
  3222.   if (pretty_flag==0)
  3223.   {   LowKey_Menu(menu_choices,EntryR,ChoiceR);
  3224.       rfalse;
  3225.   }
  3226.   menu_nesting++;
  3227.   menu_item=0;
  3228.   lines=indirect(EntryR);
  3229.   main_title=item_name; main_wid=item_width;
  3230.   cl=7;
  3231.   .ReDisplay;
  3232.       oldcl=0;
  3233.       @erase_window $ffff;
  3234.       i=lines+7;
  3235.       @split_window i;
  3236.       i = 0->33;
  3237.       if (i==0) i=80;
  3238.       @set_window 1;
  3239.       @set_cursor 1 1;
  3240.       style reverse;
  3241.       spaces(i); j=i/2-main_wid;
  3242.       @set_cursor 1 j;
  3243.       print_paddr main_title;
  3244.       @set_cursor 2 1; spaces(i);
  3245.       @set_cursor 2 2; print "N = next subject";
  3246.       j=i-12; @set_cursor 2 j; print "P = previous";
  3247.       @set_cursor 3 1; spaces(i);
  3248.       @set_cursor 3 2; print "RETURN = read subject";
  3249.       j=i-17; @set_cursor 3 j;
  3250.       if (menu_nesting==1)
  3251.           print "  Q = resume game";
  3252.       else
  3253.           print "Q = previous menu";
  3254.       style roman;
  3255.       @set_cursor 5 2; font off;
  3256.  
  3257.       print_paddr menu_choices;
  3258.  
  3259.       .KeyLoop;
  3260.       if (cl~=oldcl)
  3261.       {   if (oldcl>0) { @set_cursor oldcl 4; print " "; }
  3262.           @set_cursor cl 4; print ">";
  3263.       }
  3264.       oldcl=cl;
  3265.       @read_char 1 0 0 pkey;
  3266.       if (pkey=='N' or 'n' or 130)
  3267.           { cl++; if (cl==7+lines) cl=7; jump KeyLoop; }
  3268.       if (pkey=='P' or 'p' or 129)
  3269.           { cl--; if (cl==6)  cl=6+lines; jump KeyLoop; }
  3270.       if (pkey=='Q' or 'q' or 27) { jump QuitHelp; }
  3271.       if (pkey==10 or 13)
  3272.       {   @set_window 0; font on;
  3273.           new_line; new_line; new_line;
  3274.  
  3275.           menu_item=cl-6;
  3276.           indirect(EntryR);
  3277.  
  3278.           @erase_window $ffff;
  3279.           @split_window 1;
  3280.           i = 0->33; if (i==0) { i=80; }
  3281.           @set_window 1; @set_cursor 1 1; style reverse; spaces(i);
  3282.           j=i/2-item_width;
  3283.           @set_cursor 1 j;
  3284.           print_paddr item_name;
  3285.           style roman; @set_window 0; new_line;
  3286.  
  3287.           i = indirect(ChoiceR);
  3288.           if (i==2) jump ReDisplay;
  3289.           if (i==3) jump QuitHelp;
  3290.  
  3291.           print "^[Please press SPACE.]^";
  3292.           @read_char 1 0 0 pkey; jump ReDisplay;
  3293.       }
  3294.       jump KeyLoop;
  3295.       .QuitHelp;
  3296.       menu_nesting--; if (menu_nesting>0) rfalse;
  3297.       font on; @set_cursor 1 1;
  3298.       @erase_window $ffff; @set_window 0;
  3299.       new_line; new_line; new_line;
  3300.       if (deadflag==0) <<Look>>;
  3301. ];  
  3302. #ENDIF;
  3303.  
  3304. Default MAX_TIMERS  64;
  3305. Global active_timers = 0;
  3306. Global the_timers  data MAX_TIMERS;
  3307. Global timer_flags data MAX_TIMERS;
  3308.  
  3309. [ TimerE; "** Too many timers/daemons! Increase MAX_TIMERS **"; ];
  3310. [ TimerE2 obj; print "** Object "; PrintShortName(obj);
  3311.       " has no time_left property! **"; ];
  3312. [ TimerE3 obj; print "** Object "; PrintShortName(obj);
  3313.       " both timer and daemon! **"; ];
  3314.  
  3315. [ StartTimer obj timer i;
  3316.    for (i=0:i<active_timers:i++)
  3317.        if (the_timers-->i==obj)
  3318.        {   if (timer_flags->i==2) TimerE3(obj);
  3319.            rfalse;
  3320.        }
  3321.    for (i=0:i<active_timers:i++)
  3322.        if (the_timers-->i==0) jump FoundTSlot;
  3323.    i=active_timers++;
  3324.    if (i*2>=MAX_TIMERS) TimerE();
  3325.    .FoundTSlot;
  3326.    if (obj.&time_left==0) TimerE2(obj);
  3327.    the_timers-->i=obj; timer_flags->i=1; obj.time_left=timer;
  3328. ];
  3329.  
  3330. [ StopTimer obj i;
  3331.    for (i=0:i<active_timers:i++)
  3332.        if (the_timers-->i==obj) jump FoundTSlot2;
  3333.    rfalse;
  3334.    .FoundTSlot2;
  3335.    if (obj.&time_left==0) TimerE2(obj);
  3336.    the_timers-->i=0; obj.time_left=0;
  3337. ];
  3338.  
  3339. [ StartDaemon obj i;
  3340.    for (i=0:i<active_timers:i++)
  3341.        if (the_timers-->i==obj)
  3342.        {   if (timer_flags->i==1) TimerE3(obj);
  3343.            rfalse;
  3344.        }
  3345.    for (i=0:i<active_timers:i++)
  3346.        if (the_timers-->i==0) jump FoundTSlot3;
  3347.    i=active_timers++;
  3348.    if (i*2>=MAX_TIMERS) TimerE();
  3349.    .FoundTSlot3;
  3350.    the_timers-->i=obj; timer_flags->i=2;
  3351. ];
  3352.  
  3353. [ StopDaemon obj i;
  3354.    for (i=0:i<active_timers:i++)
  3355.        if (the_timers-->i==obj) jump FoundTSlot4;
  3356.    rfalse;
  3357.    .FoundTSlot4;
  3358.    the_timers-->i=0;
  3359. ];
  3360.  
  3361. [ Time i j;
  3362.  
  3363.    turns++;
  3364.    if (the_time~=NULL)
  3365.    {   if (time_rate>=0) the_time=the_time+time_rate;
  3366.        else
  3367.        {   time_step--;
  3368.            if (time_step==0)
  3369.            {   the_time++;
  3370.                time_step = -time_rate;
  3371.            }
  3372.        }
  3373.        the_time=the_time % 1440;
  3374.    }
  3375. #IFDEF DEBUG;
  3376.    if (debug_flag & 4 ~= 0)
  3377.    {   for (i=0: i<active_timers: i++)
  3378.        {   j=the_timers-->i;
  3379.            if (j~=0)
  3380.            {   PrintShortName(j);
  3381.                if (timer_flags->i==2) print ": daemon";
  3382.                else
  3383.                { print ": timer with ", j.time_left, " turns to go"; }
  3384.                new_line;
  3385.            }
  3386.        }
  3387.    }
  3388. #ENDIF;
  3389.    for (i=0: deadflag==0 && i<active_timers: i++)
  3390.    {   j=the_timers-->i;
  3391.        if (j~=0)
  3392.        {   if (timer_flags->i==2) RunRoutines(j,daemon);
  3393.            else
  3394.            {   if (j.time_left==0)
  3395.                {   StopTimer(j);
  3396.                    RunRoutines(j,time_out);
  3397.                }
  3398.                else
  3399.                    j.time_left=j.time_left-1;
  3400.            }
  3401.        }
  3402.    }
  3403.    if (deadflag==0)
  3404.    {   et_flag=1; verb_word=0;
  3405.        DoEachTurn(location);
  3406.        SearchScope(location,player,0);
  3407.        et_flag=0;
  3408.    }
  3409.    if (deadflag==0) TimePasses();
  3410.    if (deadflag==0)
  3411.    {   AdjustLight();
  3412.        objectloop (i in player)
  3413.            if (i hasnt moved)
  3414.            {   give i moved;
  3415.                if (i has scored)
  3416.                {   score=score+OBJECT_SCORE;
  3417.                    things_score=things_score+OBJECT_SCORE;
  3418.                }
  3419.            }
  3420.    }
  3421. ];
  3422.  
  3423. [ AdjustLight flag i;
  3424.    i=lightflag;
  3425.    lightflag=OffersLight(parent(player));
  3426.  
  3427.    if (i==0 && lightflag==1)
  3428.    {   location=real_location; if (flag==0) <Look>;
  3429.    }
  3430.  
  3431.    if (i==1 && lightflag==0)
  3432.    {   real_location=location; location=thedark;
  3433.        if (flag==0) return L__M(##Miscellany, 9);
  3434.    }
  3435.  
  3436.    if (i==0 && lightflag==0) location=thedark;
  3437. ];
  3438.  
  3439. [ OffersLight i j;
  3440.    if (i==0) rfalse;
  3441.    if (i has light) rtrue;
  3442.    objectloop (j in i)
  3443.        if (HasLightSource(j)==1) rtrue;
  3444.    if (i has supporter || i has transparent ||
  3445.        i has enterable || (i has container && i has open))
  3446.        return OffersLight(parent(i));
  3447.    rfalse;
  3448. ];
  3449.  
  3450. [ HasLightSource i j ad;
  3451.    if (i==0) rfalse;
  3452.    if (i has light) rtrue;
  3453.    if (i has supporter || i has transparent ||
  3454.        i has enterable || (i has container && i has open))
  3455.    {   objectloop (i in i)
  3456.            if (HasLightSource(i)==1) rtrue;
  3457.    }
  3458.    ad = i.&add_to_scope;
  3459.    if (parent(i)~=0 && ad ~= 0)
  3460.    {   if (ad-->0 > top_object)
  3461.        {   ats_hls = 0; ats_flag = 1;
  3462.            RunRoutines(i, add_to_scope);
  3463.            ats_flag = 0; if (ats_hls == 1) rtrue;
  3464.        }
  3465.        else
  3466.        {   for (j=0:(2*j)<i.#add_to_scope:j++)
  3467.                if (HasLightSource(ad-->j)==1) rtrue;
  3468.        }
  3469.    }
  3470.    rfalse;
  3471. ];
  3472.  
  3473. [ SayProS x;
  3474.   if (x==0) print "is unset";
  3475.   else { print "means "; DefArt(x); }
  3476. ];
  3477.  
  3478. [ PronounsSub;
  3479.   print "At the moment, ~it~ "; SayProS(itobj);
  3480.   print ", ~him~ "; SayProS(himobj);
  3481.   if (player==selfobj) print " and"; else print ",";
  3482.   print " ~her~ "; SayProS(herobj);
  3483.   if (player==selfobj) ".";
  3484.   print " and ~me~ means ", object player; ".";
  3485. ];
  3486.  
  3487. [ ChangePlayer obj flag i;
  3488.   if (obj.&number==0) "** Player objects must have ~number~ prop **";
  3489.   give player ~transparent ~concealed;
  3490.   i=obj; while(parent(i)~=0) { if (i has animate) give i transparent;
  3491.                                i=parent(i); }
  3492.   if (player==selfobj) player.short_name="your former self";
  3493.   player.number=real_location; player=obj;
  3494.   if (player==selfobj) player.short_name=NULL;
  3495.   give player transparent concealed animate proper;
  3496.   i=player; while(parent(i)~=0) i=parent(i); location=i;
  3497.   real_location=player.number;
  3498.   if (real_location==0) real_location=location;
  3499.   lightflag=OffersLight(parent(player));
  3500.   if (lightflag==0) location=thedark;
  3501.   print_player_flag=flag;
  3502. ];
  3503.  
  3504. [ ChangeDefault prop val;
  3505.    (0-->5)-->(prop-1) = val;
  3506. ];
  3507.  
  3508. [ RandomEntry tab;
  3509.   if (tab-->0==0) "** Table size 0 **";
  3510.   return tab-->(random(tab-->0));
  3511. ];
  3512.  
  3513. [ Indefart o;
  3514.    if (o hasnt proper) { PrintOrRun(o,article,1); print " "; }
  3515.    PrintShortName(o);
  3516. ];
  3517.  
  3518. [ Defart o;
  3519.    if (o hasnt proper) print "the "; PrintShortName(o);
  3520. ];
  3521.  
  3522. [ CDefart o;
  3523.    if (o hasnt proper) print "The "; PrintShortName(o);
  3524. ];
  3525.  
  3526. [ PrintShortName o;
  3527.    if (o==0) { print "nothing"; rtrue; }
  3528.    if (o>top_object || o<0) { print "<no such object>"; rtrue; }
  3529.    if (o==player) { print "yourself"; rtrue; }
  3530.    if (o.&short_name~=0 && PrintOrRun(o,short_name,1)~=0) rtrue;
  3531.    print_obj o;
  3532. ];
  3533.  
  3534. ! Provided for, e.g.,  print (DirectionName) obj.door_dir;
  3535.  
  3536. [ DirectionName d;
  3537.    switch(d)
  3538.    {   n_to: print "north";
  3539.        s_to: print "south";
  3540.        e_to: print "east";
  3541.        w_to: print "west";
  3542.        ne_to: print "northeast";
  3543.        nw_to: print "northwest";
  3544.        se_to: print "southeast";
  3545.        sw_to: print "southwest";
  3546.        u_to: print "up";
  3547.        d_to: print "down";
  3548.        in_to: print "in";
  3549.        out_to: print "out";
  3550.        default: "** No such direction **";
  3551.    }
  3552. ];
  3553.  
  3554. ! ----------------------------------------------------------------------------
  3555.